blob: 10147de0897d7be956e014ce120759fe55144af3 [file] [log] [blame]
Wolfgang Denk46263f22013-07-28 22:12:45 +02001/*
Wolfgang Denk1b387ef2013-09-17 11:24:06 +02002 * SPDX-License-Identifier: GPL-2.0 IBM-pibs
Wolfgang Denk46263f22013-07-28 22:12:45 +02003 */
wdenkaffae2b2002-08-17 09:36:01 +00004/*-----------------------------------------------------------------------------+
5 |
Stefan Roesed6c61aa2005-08-16 18:18:00 +02006 | File Name: miiphy.c
wdenkaffae2b2002-08-17 09:36:01 +00007 |
Stefan Roesed6c61aa2005-08-16 18:18:00 +02008 | Function: This module has utilities for accessing the MII PHY through
wdenkaffae2b2002-08-17 09:36:01 +00009 | the EMAC3 macro.
10 |
Stefan Roesed6c61aa2005-08-16 18:18:00 +020011 | Author: Mark Wisner
wdenkaffae2b2002-08-17 09:36:01 +000012 |
wdenkaffae2b2002-08-17 09:36:01 +000013 +-----------------------------------------------------------------------------*/
14
Stefan Roesec3307fa2008-02-19 21:58:25 +010015/* define DEBUG for debugging output (obviously ;-)) */
16#if 0
17#define DEBUG
18#endif
19
wdenkaffae2b2002-08-17 09:36:01 +000020#include <common.h>
21#include <asm/processor.h>
Stefan Roese2d834762007-10-23 14:03:17 +020022#include <asm/io.h>
wdenkaffae2b2002-08-17 09:36:01 +000023#include <ppc_asm.tmpl>
24#include <commproc.h>
Stefan Roeseb36df562010-09-09 19:18:00 +020025#include <asm/ppc4xx-emac.h>
26#include <asm/ppc4xx-mal.h>
wdenkaffae2b2002-08-17 09:36:01 +000027#include <miiphy.h>
28
Stefan Roesec3307fa2008-02-19 21:58:25 +010029#if !defined(CONFIG_PHY_CLK_FREQ)
30#define CONFIG_PHY_CLK_FREQ 0
31#endif
32
wdenkaffae2b2002-08-17 09:36:01 +000033/***********************************************************/
Stefan Roesed6c61aa2005-08-16 18:18:00 +020034/* Dump out to the screen PHY regs */
wdenkaffae2b2002-08-17 09:36:01 +000035/***********************************************************/
36
Marian Balakowicz63ff0042005-10-28 22:30:33 +020037void miiphy_dump (char *devname, unsigned char addr)
wdenkaffae2b2002-08-17 09:36:01 +000038{
39 unsigned long i;
40 unsigned short data;
41
wdenkaffae2b2002-08-17 09:36:01 +000042 for (i = 0; i < 0x1A; i++) {
Marian Balakowicz63ff0042005-10-28 22:30:33 +020043 if (miiphy_read (devname, addr, i, &data)) {
wdenkaffae2b2002-08-17 09:36:01 +000044 printf ("read error for reg %lx\n", i);
45 return;
46 }
47 printf ("Phy reg %lx ==> %4x\n", i, data);
48
49 /* jump to the next set of regs */
50 if (i == 0x07)
51 i = 0x0f;
52
Stefan Roesed6c61aa2005-08-16 18:18:00 +020053 } /* end for loop */
54} /* end dump */
wdenkaffae2b2002-08-17 09:36:01 +000055
wdenkaffae2b2002-08-17 09:36:01 +000056/***********************************************************/
Stefan Roesed6c61aa2005-08-16 18:18:00 +020057/* (Re)start autonegotiation */
wdenkaffae2b2002-08-17 09:36:01 +000058/***********************************************************/
Marian Balakowicz63ff0042005-10-28 22:30:33 +020059int phy_setup_aneg (char *devname, unsigned char addr)
wdenkaffae2b2002-08-17 09:36:01 +000060{
Larry Johnsonc3485782007-12-27 10:50:55 -050061 u16 bmcr;
wdenkaffae2b2002-08-17 09:36:01 +000062
Larry Johnsonc3485782007-12-27 10:50:55 -050063#if defined(CONFIG_PHY_DYNAMIC_ANEG)
64 /*
65 * Set up advertisement based on capablilities reported by the PHY.
66 * This should work for both copper and fiber.
67 */
68 u16 bmsr;
69#if defined(CONFIG_PHY_GIGE)
70 u16 exsr = 0x0000;
71#endif
72
Mike Frysinger8ef583a2010-12-23 15:40:12 -050073 miiphy_read (devname, addr, MII_BMSR, &bmsr);
Larry Johnsonc3485782007-12-27 10:50:55 -050074
75#if defined(CONFIG_PHY_GIGE)
Mike Frysinger8ef583a2010-12-23 15:40:12 -050076 if (bmsr & BMSR_ESTATEN)
77 miiphy_read (devname, addr, MII_ESTATUS, &exsr);
Larry Johnsonc3485782007-12-27 10:50:55 -050078
Mike Frysinger8ef583a2010-12-23 15:40:12 -050079 if (exsr & (ESTATUS_1000XF | ESTATUS_1000XH)) {
Larry Johnsonc3485782007-12-27 10:50:55 -050080 /* 1000BASE-X */
81 u16 anar = 0x0000;
82
Mike Frysinger8ef583a2010-12-23 15:40:12 -050083 if (exsr & ESTATUS_1000XF)
Stefan Roesec722c702011-01-10 12:56:13 +010084 anar |= ADVERTISE_1000XFULL;
Larry Johnsonc3485782007-12-27 10:50:55 -050085
Mike Frysinger8ef583a2010-12-23 15:40:12 -050086 if (exsr & ESTATUS_1000XH)
87 anar |= ADVERTISE_1000XHALF;
Larry Johnsonc3485782007-12-27 10:50:55 -050088
Mike Frysinger8ef583a2010-12-23 15:40:12 -050089 miiphy_write (devname, addr, MII_ADVERTISE, anar);
Larry Johnsonc3485782007-12-27 10:50:55 -050090 } else
91#endif
92 {
93 u16 anar, btcr;
94
Mike Frysinger8ef583a2010-12-23 15:40:12 -050095 miiphy_read (devname, addr, MII_ADVERTISE, &anar);
96 anar &= ~(0x5000 | LPA_100BASE4 | LPA_100FULL |
97 LPA_100HALF | LPA_10FULL | LPA_10HALF);
Larry Johnsonc3485782007-12-27 10:50:55 -050098
Mike Frysinger8ef583a2010-12-23 15:40:12 -050099 miiphy_read (devname, addr, MII_CTRL1000, &btcr);
Larry Johnsonc3485782007-12-27 10:50:55 -0500100 btcr &= ~(0x00FF | PHY_1000BTCR_1000FD | PHY_1000BTCR_1000HD);
101
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500102 if (bmsr & BMSR_100BASE4)
103 anar |= LPA_100BASE4;
Larry Johnsonc3485782007-12-27 10:50:55 -0500104
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500105 if (bmsr & BMSR_100FULL)
106 anar |= LPA_100FULL;
Larry Johnsonc3485782007-12-27 10:50:55 -0500107
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500108 if (bmsr & BMSR_100HALF)
109 anar |= LPA_100HALF;
Larry Johnsonc3485782007-12-27 10:50:55 -0500110
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500111 if (bmsr & BMSR_10FULL)
112 anar |= LPA_10FULL;
Larry Johnsonc3485782007-12-27 10:50:55 -0500113
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500114 if (bmsr & BMSR_10HALF)
115 anar |= LPA_10HALF;
Larry Johnsonc3485782007-12-27 10:50:55 -0500116
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500117 miiphy_write (devname, addr, MII_ADVERTISE, anar);
Larry Johnsonc3485782007-12-27 10:50:55 -0500118
119#if defined(CONFIG_PHY_GIGE)
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500120 if (exsr & ESTATUS_1000_TFULL)
Larry Johnsonc3485782007-12-27 10:50:55 -0500121 btcr |= PHY_1000BTCR_1000FD;
122
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500123 if (exsr & ESTATUS_1000_THALF)
Larry Johnsonc3485782007-12-27 10:50:55 -0500124 btcr |= PHY_1000BTCR_1000HD;
125
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500126 miiphy_write (devname, addr, MII_CTRL1000, btcr);
Larry Johnsonc3485782007-12-27 10:50:55 -0500127#endif
128 }
129
130#else /* defined(CONFIG_PHY_DYNAMIC_ANEG) */
131 /*
132 * Set up standard advertisement
133 */
134 u16 adv;
135
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500136 miiphy_read (devname, addr, MII_ADVERTISE, &adv);
137 adv |= (LPA_LPACK | LPA_100FULL | LPA_100HALF |
138 LPA_10FULL | LPA_10HALF);
139 miiphy_write (devname, addr, MII_ADVERTISE, adv);
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200140
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500141 miiphy_read (devname, addr, MII_CTRL1000, &adv);
Marian Balakowicz6c5879f2006-06-30 16:30:46 +0200142 adv |= (0x0300);
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500143 miiphy_write (devname, addr, MII_CTRL1000, adv);
Marian Balakowicz6c5879f2006-06-30 16:30:46 +0200144
Larry Johnsonc3485782007-12-27 10:50:55 -0500145#endif /* defined(CONFIG_PHY_DYNAMIC_ANEG) */
146
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200147 /* Start/Restart aneg */
Mike Frysinger8ef583a2010-12-23 15:40:12 -0500148 miiphy_read (devname, addr, MII_BMCR, &bmcr);
149 bmcr |= (BMCR_ANENABLE | BMCR_ANRESTART);
150 miiphy_write (devname, addr, MII_BMCR, bmcr);
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200151
152 return 0;
153}
154
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200155/***********************************************************/
156/* read a phy reg and return the value with a rc */
157/***********************************************************/
Stefan Roesec3307fa2008-02-19 21:58:25 +0100158/* AMCC_TODO:
159 * Find out of the choice for the emac for MDIO is from the bridges,
160 * i.e. ZMII or RGMII as approporiate. If the bridges are not used
161 * to determine the emac for MDIO, then is the SDR0_ETH_CFG[MDIO_SEL]
162 * used? If so, then this routine below does not apply to the 460EX/GT.
163 *
164 * sr: Currently on 460EX only EMAC0 works with MDIO, so we always
165 * return EMAC0 offset here
Victor Gallardo78d78232008-09-04 23:49:36 -0700166 * vg: For 460EX/460GT if internal GPCS PHY address is specified
167 * return appropriate EMAC offset
Stefan Roesec3307fa2008-02-19 21:58:25 +0100168 */
Victor Gallardo78d78232008-09-04 23:49:36 -0700169unsigned int miiphy_getemac_offset(u8 addr)
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200170{
Mike Frysingere2a53452011-10-02 10:01:27 +0000171#if defined(CONFIG_440) && \
Stefan Roesec3307fa2008-02-19 21:58:25 +0100172 !defined(CONFIG_440SP) && !defined(CONFIG_440SPE) && \
Mike Frysingere2a53452011-10-02 10:01:27 +0000173 !defined(CONFIG_460EX) && !defined(CONFIG_460GT)
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200174 unsigned long zmii;
175 unsigned long eoffset;
176
177 /* Need to find out which mdi port we're using */
Niklaus Gigerddc922f2009-10-04 20:04:20 +0200178 zmii = in_be32((void *)ZMII0_FER);
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200179
Larry Johnsonc3485782007-12-27 10:50:55 -0500180 if (zmii & (ZMII_FER_MDI << ZMII_FER_V (0)))
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200181 /* using port 0 */
182 eoffset = 0;
Larry Johnsonc3485782007-12-27 10:50:55 -0500183
184 else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (1)))
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200185 /* using port 1 */
186 eoffset = 0x100;
Larry Johnsonc3485782007-12-27 10:50:55 -0500187
188 else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (2)))
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200189 /* using port 2 */
190 eoffset = 0x400;
Larry Johnsonc3485782007-12-27 10:50:55 -0500191
192 else if (zmii & (ZMII_FER_MDI << ZMII_FER_V (3)))
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200193 /* using port 3 */
194 eoffset = 0x600;
Larry Johnsonc3485782007-12-27 10:50:55 -0500195
196 else {
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200197 /* None of the mdi ports are enabled! */
198 /* enable port 0 */
199 zmii |= ZMII_FER_MDI << ZMII_FER_V (0);
Niklaus Gigerddc922f2009-10-04 20:04:20 +0200200 out_be32((void *)ZMII0_FER, zmii);
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200201 eoffset = 0;
202 /* need to soft reset port 0 */
Niklaus Gigerddc922f2009-10-04 20:04:20 +0200203 zmii = in_be32((void *)EMAC0_MR0);
204 zmii |= EMAC_MR0_SRST;
205 out_be32((void *)EMAC0_MR0, zmii);
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200206 }
207
208 return (eoffset);
209#else
Stefan Roesedbbd1252007-10-05 17:10:59 +0200210
Mike Frysingere2a53452011-10-02 10:01:27 +0000211#if defined(CONFIG_405EX)
Stefan Roesedbbd1252007-10-05 17:10:59 +0200212 unsigned long rgmii;
213 int devnum = 1;
214
Stefan Roese2d834762007-10-23 14:03:17 +0200215 rgmii = in_be32((void *)RGMII_FER);
Stefan Roesedbbd1252007-10-05 17:10:59 +0200216 if (rgmii & (1 << (19 - devnum)))
217 return 0x100;
218#endif
219
Victor Gallardo78d78232008-09-04 23:49:36 -0700220#if defined(CONFIG_460EX) || defined(CONFIG_460GT)
Victor Gallardo78d78232008-09-04 23:49:36 -0700221 u32 eoffset = 0;
222
223 switch (addr) {
224#if defined(CONFIG_HAS_ETH1) && defined(CONFIG_GPCS_PHY1_ADDR)
225 case CONFIG_GPCS_PHY1_ADDR:
Niklaus Gigerddc922f2009-10-04 20:04:20 +0200226 if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x100)))
Victor Gallardo78d78232008-09-04 23:49:36 -0700227 eoffset = 0x100;
228 break;
229#endif
230#if defined(CONFIG_HAS_ETH2) && defined(CONFIG_GPCS_PHY2_ADDR)
231 case CONFIG_GPCS_PHY2_ADDR:
Niklaus Gigerddc922f2009-10-04 20:04:20 +0200232 if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x300)))
Victor Gallardo78d78232008-09-04 23:49:36 -0700233 eoffset = 0x300;
234 break;
235#endif
236#if defined(CONFIG_HAS_ETH3) && defined(CONFIG_GPCS_PHY3_ADDR)
237 case CONFIG_GPCS_PHY3_ADDR:
Niklaus Gigerddc922f2009-10-04 20:04:20 +0200238 if (addr == EMAC_MR1_IPPA_GET(in_be32((void *)EMAC0_MR1 + 0x400)))
Victor Gallardo78d78232008-09-04 23:49:36 -0700239 eoffset = 0x400;
240 break;
241#endif
242 default:
243 eoffset = 0;
244 break;
245 }
246 return eoffset;
247#endif
248
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200249 return 0;
250#endif
251}
252
Stefan Roesec3307fa2008-02-19 21:58:25 +0100253static int emac_miiphy_wait(u32 emac_reg)
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200254{
Stefan Roesec3307fa2008-02-19 21:58:25 +0100255 u32 sta_reg;
256 int i;
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200257
Stefan Roesec3307fa2008-02-19 21:58:25 +0100258 /* wait for completion */
wdenkaffae2b2002-08-17 09:36:01 +0000259 i = 0;
Stefan Roesec3307fa2008-02-19 21:58:25 +0100260 do {
Niklaus Gigerddc922f2009-10-04 20:04:20 +0200261 sta_reg = in_be32((void *)EMAC0_STACR + emac_reg);
Stefan Roesec3307fa2008-02-19 21:58:25 +0100262 if (i++ > 5) {
Niklaus Gigerddc922f2009-10-04 20:04:20 +0200263 debug("%s [%d]: Timeout! EMAC0_STACR=0x%0x\n", __func__,
Stefan Roesec3307fa2008-02-19 21:58:25 +0100264 __LINE__, sta_reg);
wdenkaffae2b2002-08-17 09:36:01 +0000265 return -1;
266 }
Stefan Roesec3307fa2008-02-19 21:58:25 +0100267 udelay(10);
268 } while ((sta_reg & EMAC_STACR_OC) == EMAC_STACR_OC_MASK);
269
270 return 0;
271}
272
273static int emac_miiphy_command(u8 addr, u8 reg, int cmd, u16 value)
274{
275 u32 emac_reg;
276 u32 sta_reg;
277
Victor Gallardo78d78232008-09-04 23:49:36 -0700278 emac_reg = miiphy_getemac_offset(addr);
Stefan Roesec3307fa2008-02-19 21:58:25 +0100279
280 /* wait for completion */
281 if (emac_miiphy_wait(emac_reg) != 0)
282 return -1;
283
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200284 sta_reg = reg; /* reg address */
Stefan Roesec3307fa2008-02-19 21:58:25 +0100285
Wolfgang Denk8ed44d92008-10-19 02:35:50 +0200286 /* set clock (50MHz) and read flags */
Stefan Roese887e2ec2006-09-07 11:51:23 +0200287#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \
Stefan Roesedbbd1252007-10-05 17:10:59 +0200288 defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
Stefan Roesec3307fa2008-02-19 21:58:25 +0100289 defined(CONFIG_460EX) || defined(CONFIG_460GT) || \
Stefan Roesedbbd1252007-10-05 17:10:59 +0200290 defined(CONFIG_405EX)
Larry Johnsonc3485782007-12-27 10:50:55 -0500291#if defined(CONFIG_IBM_EMAC4_V4) /* EMAC4 V4 changed bit setting */
Stefan Roesec3307fa2008-02-19 21:58:25 +0100292 sta_reg = (sta_reg & ~EMAC_STACR_OP_MASK) | cmd;
Marian Balakowicz6c5879f2006-06-30 16:30:46 +0200293#else
Stefan Roesec3307fa2008-02-19 21:58:25 +0100294 sta_reg |= cmd;
Marian Balakowicz6c5879f2006-06-30 16:30:46 +0200295#endif
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200296#else
Stefan Roesec3307fa2008-02-19 21:58:25 +0100297 sta_reg = (sta_reg | cmd) & ~EMAC_STACR_CLK_100MHZ;
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200298#endif
299
Stefan Roesec3307fa2008-02-19 21:58:25 +0100300 /* Some boards (mainly 405EP based) define the PHY clock freqency fixed */
wdenk12f34242003-09-02 22:48:03 +0000301 sta_reg = sta_reg | CONFIG_PHY_CLK_FREQ;
Stefan Roesec3307fa2008-02-19 21:58:25 +0100302 sta_reg = sta_reg | ((u32)addr << 5); /* Phy address */
Marian Balakowicz6c5879f2006-06-30 16:30:46 +0200303 sta_reg = sta_reg | EMAC_STACR_OC_MASK; /* new IBM emac v4 */
Stefan Roesec3307fa2008-02-19 21:58:25 +0100304 if (cmd == EMAC_STACR_WRITE)
305 memcpy(&sta_reg, &value, 2); /* put in data */
306
Niklaus Gigerddc922f2009-10-04 20:04:20 +0200307 out_be32((void *)EMAC0_STACR + emac_reg, sta_reg);
Stefan Roesec3307fa2008-02-19 21:58:25 +0100308 debug("%s [%d]: sta_reg=%08x\n", __func__, __LINE__, sta_reg);
wdenkaffae2b2002-08-17 09:36:01 +0000309
Stefan Roesec3307fa2008-02-19 21:58:25 +0100310 /* wait for completion */
311 if (emac_miiphy_wait(emac_reg) != 0)
312 return -1;
Larry Johnsonc3485782007-12-27 10:50:55 -0500313
Stefan Roesec3307fa2008-02-19 21:58:25 +0100314 debug("%s [%d]: sta_reg=%08x\n", __func__, __LINE__, sta_reg);
Larry Johnsonc3485782007-12-27 10:50:55 -0500315 if ((sta_reg & EMAC_STACR_PHYE) != 0)
wdenkaffae2b2002-08-17 09:36:01 +0000316 return -1;
wdenkaffae2b2002-08-17 09:36:01 +0000317
wdenkaffae2b2002-08-17 09:36:01 +0000318 return 0;
Stefan Roesec3307fa2008-02-19 21:58:25 +0100319}
wdenkaffae2b2002-08-17 09:36:01 +0000320
Mike Frysinger5700bb62010-07-27 18:35:08 -0400321int emac4xx_miiphy_read (const char *devname, unsigned char addr, unsigned char reg,
Stefan Roesec3307fa2008-02-19 21:58:25 +0100322 unsigned short *value)
323{
324 unsigned long sta_reg;
325 unsigned long emac_reg;
326
Victor Gallardo78d78232008-09-04 23:49:36 -0700327 emac_reg = miiphy_getemac_offset(addr);
Stefan Roesec3307fa2008-02-19 21:58:25 +0100328
329 if (emac_miiphy_command(addr, reg, EMAC_STACR_READ, 0) != 0)
330 return -1;
331
Niklaus Gigerddc922f2009-10-04 20:04:20 +0200332 sta_reg = in_be32((void *)EMAC0_STACR + emac_reg);
Stefan Roese0b34dbb2009-09-07 10:52:24 +0200333 *value = sta_reg >> 16;
Stefan Roesec3307fa2008-02-19 21:58:25 +0100334
335 return 0;
336}
wdenkaffae2b2002-08-17 09:36:01 +0000337
wdenkaffae2b2002-08-17 09:36:01 +0000338/***********************************************************/
Stefan Roesed6c61aa2005-08-16 18:18:00 +0200339/* write a phy reg and return the value with a rc */
wdenkaffae2b2002-08-17 09:36:01 +0000340/***********************************************************/
341
Mike Frysinger5700bb62010-07-27 18:35:08 -0400342int emac4xx_miiphy_write (const char *devname, unsigned char addr, unsigned char reg,
Larry Johnsonc3485782007-12-27 10:50:55 -0500343 unsigned short value)
wdenkaffae2b2002-08-17 09:36:01 +0000344{
Stefan Roesec3307fa2008-02-19 21:58:25 +0100345 return emac_miiphy_command(addr, reg, EMAC_STACR_WRITE, value);
346}