]>
Commit | Line | Data |
---|---|---|
668e2050 TH |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * (C) Copyright 2020 | |
4 | * Tim Harvey, Gateworks Corporation | |
5 | */ | |
6 | ||
7 | #include <dm.h> | |
8 | #include <dm/device_compat.h> | |
9 | #include <dm/device-internal.h> | |
10 | #include <dm/lists.h> | |
11 | #include <eth_phy.h> | |
12 | #include <linux/delay.h> | |
13 | #include <miiphy.h> | |
14 | #include <i2c.h> | |
15 | #include <net/dsa.h> | |
16 | ||
17 | #include <asm-generic/gpio.h> | |
18 | ||
3ca49557 KW |
19 | /* Used with variable features to indicate capabilities. */ |
20 | #define NEW_XMII BIT(1) | |
21 | #define IS_9893 BIT(2) | |
22 | ||
668e2050 TH |
23 | /* Global registers */ |
24 | ||
25 | /* Chip ID */ | |
26 | #define REG_CHIP_ID0__1 0x0000 | |
27 | ||
28 | /* Operation control */ | |
29 | #define REG_SW_OPERATION 0x0300 | |
30 | #define SW_RESET BIT(1) | |
31 | #define SW_START BIT(0) | |
32 | ||
33 | /* Port Specific Registers */ | |
34 | #define PORT_CTRL_ADDR(port, addr) ((addr) | (((port) + 1) << 12)) | |
35 | ||
36 | /* Port Control */ | |
37 | #define REG_PORT_XMII_CTRL_1 0x0301 | |
38 | #define PORT_MII_NOT_1GBIT BIT(6) | |
39 | #define PORT_MII_SEL_EDGE BIT(5) | |
40 | #define PORT_RGMII_ID_IG_ENABLE BIT(4) | |
41 | #define PORT_RGMII_ID_EG_ENABLE BIT(3) | |
42 | #define PORT_MII_MAC_MODE BIT(2) | |
43 | #define PORT_MII_SEL_M 0x3 | |
44 | #define PORT_RGMII_SEL 0x0 | |
45 | #define PORT_RMII_SEL 0x1 | |
46 | #define PORT_GMII_SEL 0x2 | |
47 | #define PORT_MII_SEL 0x3 | |
3ca49557 KW |
48 | /* S1 */ |
49 | #define PORT_MII_1000MBIT_S1 BIT(6) | |
50 | /* S1 */ | |
51 | #define PORT_MII_SEL_S1 0x0 | |
52 | #define PORT_RMII_SEL_S1 0x1 | |
53 | #define PORT_GMII_SEL_S1 0x2 | |
54 | #define PORT_RGMII_SEL_S1 0x3 | |
668e2050 TH |
55 | |
56 | /* Port MSTP State Register */ | |
57 | #define REG_PORT_MSTP_STATE 0x0b04 | |
58 | #define PORT_TX_ENABLE BIT(2) | |
59 | #define PORT_RX_ENABLE BIT(1) | |
60 | #define PORT_LEARN_DISABLE BIT(0) | |
61 | ||
62 | /* MMD */ | |
63 | #define REG_PORT_PHY_MMD_SETUP 0x011A | |
64 | #define PORT_MMD_OP_MODE_M 0x3 | |
65 | #define PORT_MMD_OP_MODE_S 14 | |
66 | #define PORT_MMD_OP_INDEX 0 | |
67 | #define PORT_MMD_OP_DATA_NO_INCR 1 | |
68 | #define PORT_MMD_OP_DATA_INCR_RW 2 | |
69 | #define PORT_MMD_OP_DATA_INCR_W 3 | |
70 | #define PORT_MMD_DEVICE_ID_M 0x1F | |
71 | #define MMD_SETUP(mode, dev) (((u16)(mode) << PORT_MMD_OP_MODE_S) | (dev)) | |
72 | #define REG_PORT_PHY_MMD_INDEX_DATA 0x011C | |
73 | ||
74 | struct ksz_dsa_priv { | |
75 | struct udevice *dev; | |
3ca49557 KW |
76 | |
77 | u32 features; /* chip specific features */ | |
668e2050 TH |
78 | }; |
79 | ||
80 | static inline int ksz_read8(struct udevice *dev, u32 reg, u8 *val) | |
81 | { | |
82 | int ret = dm_i2c_read(dev, reg, val, 1); | |
83 | ||
84 | dev_dbg(dev, "%s 0x%04x<<0x%02x\n", __func__, reg, *val); | |
85 | ||
86 | return ret; | |
87 | } | |
88 | ||
89 | static inline int ksz_pread8(struct udevice *dev, int port, int reg, u8 *val) | |
90 | { | |
91 | return ksz_read8(dev, PORT_CTRL_ADDR(port, reg), val); | |
92 | } | |
93 | ||
94 | static inline int ksz_write8(struct udevice *dev, u32 reg, u8 val) | |
95 | { | |
96 | dev_dbg(dev, "%s 0x%04x>>0x%02x\n", __func__, reg, val); | |
97 | return dm_i2c_write(dev, reg, &val, 1); | |
98 | } | |
99 | ||
100 | static inline int ksz_pwrite8(struct udevice *dev, int port, int reg, u8 val) | |
101 | { | |
102 | return ksz_write8(dev, PORT_CTRL_ADDR(port, reg), val); | |
103 | } | |
104 | ||
105 | static inline int ksz_write16(struct udevice *dev, u32 reg, u16 val) | |
106 | { | |
107 | u8 buf[2]; | |
108 | ||
109 | buf[1] = val & 0xff; | |
110 | buf[0] = val >> 8; | |
111 | dev_dbg(dev, "%s 0x%04x>>0x%04x\n", __func__, reg, val); | |
112 | ||
113 | return dm_i2c_write(dev, reg, buf, 2); | |
114 | } | |
115 | ||
116 | static inline int ksz_pwrite16(struct udevice *dev, int port, int reg, u16 val) | |
117 | { | |
118 | return ksz_write16(dev, PORT_CTRL_ADDR(port, reg), val); | |
119 | } | |
120 | ||
121 | static inline int ksz_read16(struct udevice *dev, u32 reg, u16 *val) | |
122 | { | |
123 | u8 buf[2]; | |
124 | int ret; | |
125 | ||
126 | ret = dm_i2c_read(dev, reg, buf, 2); | |
127 | *val = (buf[0] << 8) | buf[1]; | |
128 | dev_dbg(dev, "%s 0x%04x<<0x%04x\n", __func__, reg, *val); | |
129 | ||
130 | return ret; | |
131 | } | |
132 | ||
133 | static inline int ksz_pread16(struct udevice *dev, int port, int reg, u16 *val) | |
134 | { | |
135 | return ksz_read16(dev, PORT_CTRL_ADDR(port, reg), val); | |
136 | } | |
137 | ||
138 | static inline int ksz_read32(struct udevice *dev, u32 reg, u32 *val) | |
139 | { | |
140 | return dm_i2c_read(dev, reg, (u8 *)val, 4); | |
141 | } | |
142 | ||
143 | static inline int ksz_pread32(struct udevice *dev, int port, int reg, u32 *val) | |
144 | { | |
145 | return ksz_read32(dev, PORT_CTRL_ADDR(port, reg), val); | |
146 | } | |
147 | ||
148 | static inline int ksz_write32(struct udevice *dev, u32 reg, u32 val) | |
149 | { | |
150 | u8 buf[4]; | |
151 | ||
152 | buf[3] = val & 0xff; | |
153 | buf[2] = (val >> 24) & 0xff; | |
154 | buf[1] = (val >> 16) & 0xff; | |
155 | buf[0] = (val >> 8) & 0xff; | |
156 | dev_dbg(dev, "%s 0x%04x>>0x%04x\n", __func__, reg, val); | |
157 | ||
158 | return dm_i2c_write(dev, reg, buf, 4); | |
159 | } | |
160 | ||
161 | static inline int ksz_pwrite32(struct udevice *dev, int port, int reg, u32 val) | |
162 | { | |
163 | return ksz_write32(dev, PORT_CTRL_ADDR(port, reg), val); | |
164 | } | |
165 | ||
166 | static __maybe_unused void ksz_port_mmd_read(struct udevice *dev, int port, | |
167 | u8 addr, u16 reg, u16 *val) | |
168 | { | |
169 | ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_SETUP, MMD_SETUP(PORT_MMD_OP_INDEX, addr)); | |
170 | ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_INDEX_DATA, reg); | |
171 | ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_SETUP, MMD_SETUP(PORT_MMD_OP_DATA_NO_INCR, addr)); | |
172 | ksz_pread16(dev, port, REG_PORT_PHY_MMD_INDEX_DATA, val); | |
173 | dev_dbg(dev, "%s P%d 0x%02x:0x%04x<<0x%04x\n", __func__, port + 1, addr, reg, *val); | |
174 | } | |
175 | ||
176 | static void ksz_port_mmd_write(struct udevice *dev, int port, u8 addr, u16 reg, u16 val) | |
177 | { | |
178 | dev_dbg(dev, "%s P%d 0x%02x:0x%04x>>0x%04x\n", __func__, port + 1, addr, addr, val); | |
179 | ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_SETUP, MMD_SETUP(PORT_MMD_OP_INDEX, addr)); | |
180 | ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_INDEX_DATA, addr); | |
181 | ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_SETUP, MMD_SETUP(PORT_MMD_OP_DATA_NO_INCR, addr)); | |
182 | ksz_pwrite16(dev, port, REG_PORT_PHY_MMD_INDEX_DATA, val); | |
183 | } | |
184 | ||
185 | /* Apply PHY settings to address errata listed in KSZ9477, KSZ9897, KSZ9896, KSZ9567 | |
186 | * Silicon Errata and Data Sheet Clarification documents | |
187 | */ | |
188 | static void ksz_phy_errata_setup(struct udevice *dev, int port) | |
189 | { | |
190 | dev_dbg(dev, "%s P%d\n", __func__, port + 1); | |
191 | ||
192 | /* Register settings are needed to improve PHY receive performance */ | |
193 | ksz_port_mmd_write(dev, port, 0x01, 0x6f, 0xdd0b); | |
194 | ksz_port_mmd_write(dev, port, 0x01, 0x8f, 0x6032); | |
195 | ksz_port_mmd_write(dev, port, 0x01, 0x9d, 0x248c); | |
196 | ksz_port_mmd_write(dev, port, 0x01, 0x75, 0x0060); | |
197 | ksz_port_mmd_write(dev, port, 0x01, 0xd3, 0x7777); | |
198 | ksz_port_mmd_write(dev, port, 0x1c, 0x06, 0x3008); | |
199 | ksz_port_mmd_write(dev, port, 0x1c, 0x08, 0x2001); | |
200 | ||
201 | /* Transmit waveform amplitude can be improved (1000BASE-T, 100BASE-TX, 10BASE-Te) */ | |
202 | ksz_port_mmd_write(dev, port, 0x1c, 0x04, 0x00d0); | |
203 | ||
204 | /* Energy Efficient Ethernet (EEE) feature select must be manually disabled */ | |
205 | ksz_port_mmd_write(dev, port, 0x07, 0x3c, 0x0000); | |
206 | ||
207 | /* Register settings are required to meet data sheet supply current specifications */ | |
208 | ksz_port_mmd_write(dev, port, 0x1c, 0x13, 0x6eff); | |
209 | ksz_port_mmd_write(dev, port, 0x1c, 0x14, 0xe6ff); | |
210 | ksz_port_mmd_write(dev, port, 0x1c, 0x15, 0x6eff); | |
211 | ksz_port_mmd_write(dev, port, 0x1c, 0x16, 0xe6ff); | |
212 | ksz_port_mmd_write(dev, port, 0x1c, 0x17, 0x00ff); | |
213 | ksz_port_mmd_write(dev, port, 0x1c, 0x18, 0x43ff); | |
214 | ksz_port_mmd_write(dev, port, 0x1c, 0x19, 0xc3ff); | |
215 | ksz_port_mmd_write(dev, port, 0x1c, 0x1a, 0x6fff); | |
216 | ksz_port_mmd_write(dev, port, 0x1c, 0x1b, 0x07ff); | |
217 | ksz_port_mmd_write(dev, port, 0x1c, 0x1c, 0x0fff); | |
218 | ksz_port_mmd_write(dev, port, 0x1c, 0x1d, 0xe7ff); | |
219 | ksz_port_mmd_write(dev, port, 0x1c, 0x1e, 0xefff); | |
220 | ksz_port_mmd_write(dev, port, 0x1c, 0x20, 0xeeee); | |
221 | } | |
222 | ||
223 | /* | |
224 | * mii bus driver | |
225 | */ | |
226 | #define KSZ_MDIO_CHILD_DRV_NAME "ksz_mdio" | |
227 | ||
228 | struct ksz_mdio_priv { | |
229 | struct ksz_dsa_priv *ksz; | |
230 | }; | |
231 | ||
232 | static int dm_ksz_mdio_read(struct udevice *dev, int addr, int devad, int reg) | |
233 | { | |
234 | struct ksz_mdio_priv *priv = dev_get_priv(dev); | |
235 | struct ksz_dsa_priv *ksz = priv->ksz; | |
236 | u16 val = 0xffff; | |
237 | ||
238 | ksz_pread16(ksz->dev, addr, 0x100 + (reg << 1), &val); | |
239 | dev_dbg(ksz->dev, "%s P%d reg=0x%04x:0x%04x<<0x%04x\n", __func__, | |
240 | addr + 1, reg, 0x100 + (reg << 1), val); | |
241 | ||
242 | return val; | |
243 | }; | |
244 | ||
245 | static int dm_ksz_mdio_write(struct udevice *dev, int addr, int devad, int reg, u16 val) | |
246 | { | |
247 | struct ksz_mdio_priv *priv = dev_get_priv(dev); | |
248 | struct ksz_dsa_priv *ksz = priv->ksz; | |
249 | ||
250 | dev_dbg(ksz->dev, "%s P%d reg=0x%04x:%04x>>0x%04x\n", | |
251 | __func__, addr + 1, reg, 0x100 + (reg << 1), val); | |
252 | ksz_pwrite16(ksz->dev, addr, 0x100 + (reg << 1), val); | |
253 | ||
254 | return 0; | |
255 | } | |
256 | ||
257 | static const struct mdio_ops ksz_mdio_ops = { | |
258 | .read = dm_ksz_mdio_read, | |
259 | .write = dm_ksz_mdio_write, | |
260 | }; | |
261 | ||
262 | static int ksz_mdio_bind(struct udevice *dev) | |
263 | { | |
264 | char name[16]; | |
265 | static int num_devices; | |
266 | ||
267 | dev_dbg(dev, "%s\n", __func__); | |
268 | sprintf(name, "ksz-mdio-%d", num_devices++); | |
269 | device_set_name(dev, name); | |
270 | ||
271 | return 0; | |
272 | } | |
273 | ||
274 | static int ksz_mdio_probe(struct udevice *dev) | |
275 | { | |
276 | struct ksz_mdio_priv *priv = dev_get_priv(dev); | |
277 | ||
278 | dev_dbg(dev, "%s\n", __func__); | |
279 | priv->ksz = dev_get_parent_priv(dev->parent); | |
280 | ||
281 | return 0; | |
282 | } | |
283 | ||
284 | static const struct udevice_id ksz_mdio_ids[] = { | |
285 | { .compatible = "microchip,ksz-mdio" }, | |
286 | { } | |
287 | }; | |
288 | ||
289 | U_BOOT_DRIVER(ksz_mdio) = { | |
290 | .name = KSZ_MDIO_CHILD_DRV_NAME, | |
291 | .id = UCLASS_MDIO, | |
292 | .of_match = ksz_mdio_ids, | |
293 | .bind = ksz_mdio_bind, | |
294 | .probe = ksz_mdio_probe, | |
295 | .ops = &ksz_mdio_ops, | |
296 | .priv_auto = sizeof(struct ksz_mdio_priv), | |
297 | .plat_auto = sizeof(struct mdio_perdev_priv), | |
298 | }; | |
299 | ||
3ca49557 KW |
300 | static void ksz9477_set_gbit(struct ksz_dsa_priv *priv, bool gbit, u8 *data) |
301 | { | |
302 | if (priv->features & NEW_XMII) { | |
303 | if (gbit) | |
304 | *data &= ~PORT_MII_NOT_1GBIT; | |
305 | else | |
306 | *data |= PORT_MII_NOT_1GBIT; | |
307 | } else { | |
308 | if (gbit) | |
309 | *data |= PORT_MII_1000MBIT_S1; | |
310 | else | |
311 | *data &= ~PORT_MII_1000MBIT_S1; | |
312 | } | |
313 | } | |
314 | ||
315 | static void ksz9477_set_xmii(struct ksz_dsa_priv *priv, int mode, u8 *data) | |
316 | { | |
317 | u8 xmii; | |
318 | ||
319 | if (priv->features & NEW_XMII) { | |
320 | switch (mode) { | |
321 | case 0: | |
322 | xmii = PORT_MII_SEL; | |
323 | break; | |
324 | case 1: | |
325 | xmii = PORT_RMII_SEL; | |
326 | break; | |
327 | case 2: | |
328 | xmii = PORT_GMII_SEL; | |
329 | break; | |
330 | default: | |
331 | xmii = PORT_RGMII_SEL; | |
332 | break; | |
333 | } | |
334 | } else { | |
335 | switch (mode) { | |
336 | case 0: | |
337 | xmii = PORT_MII_SEL_S1; | |
338 | break; | |
339 | case 1: | |
340 | xmii = PORT_RMII_SEL_S1; | |
341 | break; | |
342 | case 2: | |
343 | xmii = PORT_GMII_SEL_S1; | |
344 | break; | |
345 | default: | |
346 | xmii = PORT_RGMII_SEL_S1; | |
347 | break; | |
348 | } | |
349 | } | |
350 | *data &= ~PORT_MII_SEL_M; | |
351 | *data |= xmii; | |
352 | } | |
353 | ||
668e2050 TH |
354 | static int ksz_port_setup(struct udevice *dev, int port, |
355 | phy_interface_t interface) | |
356 | { | |
357 | struct dsa_pdata *pdata = dev_get_uclass_plat(dev); | |
358 | u8 data8; | |
359 | ||
360 | dev_dbg(dev, "%s P%d %s\n", __func__, port + 1, | |
361 | (port == pdata->cpu_port) ? "cpu" : ""); | |
362 | ||
3ca49557 | 363 | struct ksz_dsa_priv *priv = dev_get_priv(dev); |
668e2050 | 364 | if (port != pdata->cpu_port) { |
3ca49557 KW |
365 | if (priv->features & NEW_XMII) |
366 | /* phy port: config errata and leds */ | |
367 | ksz_phy_errata_setup(dev, port); | |
668e2050 TH |
368 | } else { |
369 | /* cpu port: configure MAC interface mode */ | |
370 | ksz_pread8(dev, port, REG_PORT_XMII_CTRL_1, &data8); | |
371 | dev_dbg(dev, "%s P%d cpu interface %s\n", __func__, port + 1, | |
372 | phy_string_for_interface(interface)); | |
373 | switch (interface) { | |
374 | case PHY_INTERFACE_MODE_MII: | |
3ca49557 KW |
375 | ksz9477_set_xmii(priv, 0, &data8); |
376 | ksz9477_set_gbit(priv, false, &data8); | |
668e2050 TH |
377 | break; |
378 | case PHY_INTERFACE_MODE_RMII: | |
3ca49557 KW |
379 | ksz9477_set_xmii(priv, 1, &data8); |
380 | ksz9477_set_gbit(priv, false, &data8); | |
668e2050 TH |
381 | break; |
382 | case PHY_INTERFACE_MODE_GMII: | |
3ca49557 KW |
383 | ksz9477_set_xmii(priv, 2, &data8); |
384 | ksz9477_set_gbit(priv, true, &data8); | |
668e2050 TH |
385 | break; |
386 | default: | |
3ca49557 KW |
387 | ksz9477_set_xmii(priv, 3, &data8); |
388 | ksz9477_set_gbit(priv, true, &data8); | |
668e2050 TH |
389 | data8 &= ~PORT_RGMII_ID_IG_ENABLE; |
390 | data8 &= ~PORT_RGMII_ID_EG_ENABLE; | |
391 | if (interface == PHY_INTERFACE_MODE_RGMII_ID || | |
392 | interface == PHY_INTERFACE_MODE_RGMII_RXID) | |
393 | data8 |= PORT_RGMII_ID_IG_ENABLE; | |
394 | if (interface == PHY_INTERFACE_MODE_RGMII_ID || | |
395 | interface == PHY_INTERFACE_MODE_RGMII_TXID) | |
396 | data8 |= PORT_RGMII_ID_EG_ENABLE; | |
3ca49557 KW |
397 | if (priv->features & IS_9893) |
398 | data8 &= ~PORT_MII_MAC_MODE; | |
668e2050 TH |
399 | break; |
400 | } | |
401 | ksz_write8(dev, PORT_CTRL_ADDR(port, REG_PORT_XMII_CTRL_1), data8); | |
402 | } | |
403 | ||
404 | return 0; | |
405 | } | |
406 | ||
1416b80d TH |
407 | static int ksz_port_probe(struct udevice *dev, int port, struct phy_device *phy) |
408 | { | |
409 | int supported = PHY_GBIT_FEATURES; | |
410 | ||
411 | /* configure phy */ | |
412 | phy->supported &= supported; | |
413 | phy->advertising &= supported; | |
414 | ||
415 | return phy_config(phy); | |
416 | } | |
417 | ||
668e2050 TH |
418 | static int ksz_port_enable(struct udevice *dev, int port, struct phy_device *phy) |
419 | { | |
420 | struct dsa_pdata *pdata = dev_get_uclass_plat(dev); | |
421 | struct ksz_dsa_priv *priv = dev_get_priv(dev); | |
668e2050 TH |
422 | u8 data8; |
423 | int ret; | |
424 | ||
425 | dev_dbg(dev, "%s P%d 0x%x %s\n", __func__, port + 1, phy->phy_id, | |
426 | phy_string_for_interface(phy->interface)); | |
427 | ||
428 | /* setup this port */ | |
429 | ret = ksz_port_setup(dev, port, phy->interface); | |
430 | if (ret) { | |
431 | dev_err(dev, "port setup failed: %d\n", ret); | |
432 | return ret; | |
433 | } | |
434 | ||
435 | /* enable port forwarding for this port */ | |
436 | ksz_pread8(priv->dev, port, REG_PORT_MSTP_STATE, &data8); | |
437 | data8 &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); | |
438 | data8 |= (PORT_TX_ENABLE | PORT_RX_ENABLE); | |
439 | ksz_pwrite8(priv->dev, port, REG_PORT_MSTP_STATE, data8); | |
440 | ||
441 | /* if cpu master we are done */ | |
442 | if (port == pdata->cpu_port) | |
443 | return 0; | |
444 | ||
668e2050 TH |
445 | /* start switch */ |
446 | ksz_read8(priv->dev, REG_SW_OPERATION, &data8); | |
447 | data8 |= SW_START; | |
448 | ksz_write8(priv->dev, REG_SW_OPERATION, data8); | |
449 | ||
1416b80d | 450 | return phy_startup(phy); |
668e2050 TH |
451 | } |
452 | ||
453 | static void ksz_port_disable(struct udevice *dev, int port, struct phy_device *phy) | |
454 | { | |
455 | struct dsa_pdata *pdata = dev_get_uclass_plat(dev); | |
456 | struct ksz_dsa_priv *priv = dev_get_priv(dev); | |
457 | u8 data8; | |
458 | ||
459 | dev_dbg(dev, "%s P%d 0x%x\n", __func__, port + 1, phy->phy_id); | |
460 | ||
461 | /* can't disable CPU port without re-configuring/re-starting switch */ | |
462 | if (port == pdata->cpu_port) | |
463 | return; | |
464 | ||
465 | /* disable port */ | |
466 | ksz_pread8(priv->dev, port, REG_PORT_MSTP_STATE, &data8); | |
467 | data8 &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); | |
468 | data8 |= PORT_LEARN_DISABLE; | |
469 | ksz_pwrite8(priv->dev, port, REG_PORT_MSTP_STATE, data8); | |
470 | ||
471 | /* | |
472 | * we don't call phy_shutdown here to avoid waiting next time we use | |
473 | * the port, but the downside is that remote side will think we're | |
474 | * actively processing traffic although we are not. | |
475 | */ | |
476 | } | |
477 | ||
668e2050 | 478 | static const struct dsa_ops ksz_dsa_ops = { |
1416b80d | 479 | .port_probe = ksz_port_probe, |
668e2050 TH |
480 | .port_enable = ksz_port_enable, |
481 | .port_disable = ksz_port_disable, | |
668e2050 TH |
482 | }; |
483 | ||
484 | static int ksz_probe_mdio(struct udevice *dev) | |
485 | { | |
486 | ofnode node, mdios; | |
487 | int ret; | |
488 | ||
489 | mdios = dev_read_subnode(dev, "mdios"); | |
490 | if (ofnode_valid(mdios)) { | |
491 | ofnode_for_each_subnode(node, mdios) { | |
492 | const char *name = ofnode_get_name(node); | |
493 | struct udevice *pdev; | |
494 | ||
495 | ret = device_bind_driver_to_node(dev, | |
496 | KSZ_MDIO_CHILD_DRV_NAME, | |
497 | name, node, &pdev); | |
498 | if (ret) | |
499 | dev_err(dev, "failed to probe %s: %d\n", name, ret); | |
500 | } | |
501 | } | |
502 | ||
503 | return 0; | |
504 | } | |
505 | ||
506 | /* | |
507 | * I2C driver | |
508 | */ | |
509 | static int ksz_i2c_probe(struct udevice *dev) | |
510 | { | |
511 | struct dsa_pdata *pdata = dev_get_uclass_plat(dev); | |
512 | struct ksz_dsa_priv *priv = dev_get_priv(dev); | |
668e2050 TH |
513 | int i, ret; |
514 | u8 data8; | |
515 | u32 id; | |
516 | ||
668e2050 TH |
517 | dev_set_parent_priv(dev, priv); |
518 | ||
519 | ret = i2c_set_chip_offset_len(dev, 2); | |
520 | if (ret) { | |
521 | printf("i2c_set_chip_offset_len failed: %d\n", ret); | |
522 | return ret; | |
523 | } | |
524 | ||
525 | /* default config */ | |
526 | priv->dev = dev; | |
527 | ||
528 | /* chip level reset */ | |
529 | ksz_read8(priv->dev, REG_SW_OPERATION, &data8); | |
530 | data8 |= SW_RESET; | |
531 | ksz_write8(priv->dev, REG_SW_OPERATION, data8); | |
532 | ||
533 | /* read chip id */ | |
534 | ret = ksz_read32(dev, REG_CHIP_ID0__1, &id); | |
535 | if (ret) | |
536 | return ret; | |
537 | id = __swab32(id); | |
538 | dev_dbg(dev, "%s id=0x%08x\n", __func__, id); | |
539 | switch (id & 0xffffff00) { | |
540 | case 0x00947700: | |
541 | puts("KSZ9477S: "); | |
542 | break; | |
543 | case 0x00956700: | |
544 | puts("KSZ9567R: "); | |
545 | break; | |
546 | case 0x00989700: | |
547 | puts("KSZ9897S: "); | |
548 | break; | |
3ca49557 KW |
549 | case 0x00989300: |
550 | puts("KSZ9893R: "); | |
551 | break; | |
668e2050 TH |
552 | default: |
553 | dev_err(dev, "invalid chip id: 0x%08x\n", id); | |
554 | return -EINVAL; | |
555 | } | |
3ca49557 KW |
556 | if ((id & 0xf00) == 0x300) |
557 | priv->features |= IS_9893; | |
558 | else | |
559 | priv->features |= NEW_XMII; | |
668e2050 TH |
560 | |
561 | /* probe mdio bus */ | |
562 | ret = ksz_probe_mdio(dev); | |
563 | if (ret) | |
564 | return ret; | |
565 | ||
566 | /* disable ports by default */ | |
567 | for (i = 0; i < pdata->num_ports; i++) { | |
568 | ksz_pread8(priv->dev, i, REG_PORT_MSTP_STATE, &data8); | |
569 | data8 &= ~(PORT_TX_ENABLE | PORT_RX_ENABLE | PORT_LEARN_DISABLE); | |
570 | ksz_pwrite8(priv->dev, i, REG_PORT_MSTP_STATE, data8); | |
571 | } | |
572 | ||
668e2050 TH |
573 | return 0; |
574 | }; | |
575 | ||
576 | static const struct udevice_id ksz_i2c_ids[] = { | |
577 | { .compatible = "microchip,ksz9897" }, | |
578 | { .compatible = "microchip,ksz9477" }, | |
579 | { .compatible = "microchip,ksz9567" }, | |
3ca49557 | 580 | { .compatible = "microchip,ksz9893" }, |
668e2050 TH |
581 | { } |
582 | }; | |
583 | ||
584 | U_BOOT_DRIVER(ksz) = { | |
585 | .name = "ksz-switch", | |
586 | .id = UCLASS_DSA, | |
587 | .of_match = ksz_i2c_ids, | |
588 | .probe = ksz_i2c_probe, | |
589 | .ops = &ksz_dsa_ops, | |
590 | .priv_auto = sizeof(struct ksz_dsa_priv), | |
591 | }; |