blob: a0e94409902069d9eb7d9ab00ff084b91672b443 [file] [log] [blame]
David J. Choid0507002010-04-29 06:12:41 +00001/*
2 * drivers/net/phy/micrel.c
3 *
4 * Driver for Micrel PHYs
5 *
6 * Author: David J. Choi
7 *
David J. Choi7ab59dc2013-01-23 14:05:15 +00008 * Copyright (c) 2010-2013 Micrel, Inc.
David J. Choid0507002010-04-29 06:12:41 +00009 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version.
14 *
David J. Choi7ab59dc2013-01-23 14:05:15 +000015 * Support : Micrel Phys:
16 * Giga phys: ksz9021, ksz9031
17 * 100/10 Phys : ksz8001, ksz8721, ksz8737, ksz8041
18 * ksz8021, ksz8031, ksz8051,
19 * ksz8081, ksz8091,
20 * ksz8061,
21 * Switch : ksz8873, ksz886x
David J. Choid0507002010-04-29 06:12:41 +000022 */
23
24#include <linux/kernel.h>
25#include <linux/module.h>
26#include <linux/phy.h>
Baruch Siachd606ef32011-02-14 02:05:33 +000027#include <linux/micrel_phy.h>
Sean Cross954c3962013-08-21 01:46:12 +000028#include <linux/of.h>
Sascha Hauer1fadee02014-10-10 09:48:05 +020029#include <linux/clk.h>
David J. Choid0507002010-04-29 06:12:41 +000030
Marek Vasut212ea992012-09-23 16:58:49 +000031/* Operation Mode Strap Override */
32#define MII_KSZPHY_OMSO 0x16
Johan Hovold00aee092014-11-11 20:00:09 +010033#define KSZPHY_OMSO_B_CAST_OFF BIT(9)
34#define KSZPHY_OMSO_RMII_OVERRIDE BIT(1)
35#define KSZPHY_OMSO_MII_OVERRIDE BIT(0)
Marek Vasut212ea992012-09-23 16:58:49 +000036
Choi, David51f932c2010-06-28 15:23:41 +000037/* general Interrupt control/status reg in vendor specific block. */
38#define MII_KSZPHY_INTCS 0x1B
Johan Hovold00aee092014-11-11 20:00:09 +010039#define KSZPHY_INTCS_JABBER BIT(15)
40#define KSZPHY_INTCS_RECEIVE_ERR BIT(14)
41#define KSZPHY_INTCS_PAGE_RECEIVE BIT(13)
42#define KSZPHY_INTCS_PARELLEL BIT(12)
43#define KSZPHY_INTCS_LINK_PARTNER_ACK BIT(11)
44#define KSZPHY_INTCS_LINK_DOWN BIT(10)
45#define KSZPHY_INTCS_REMOTE_FAULT BIT(9)
46#define KSZPHY_INTCS_LINK_UP BIT(8)
Choi, David51f932c2010-06-28 15:23:41 +000047#define KSZPHY_INTCS_ALL (KSZPHY_INTCS_LINK_UP |\
48 KSZPHY_INTCS_LINK_DOWN)
49
Johan Hovold5a167782014-11-11 20:00:14 +010050/* PHY Control 1 */
51#define MII_KSZPHY_CTRL_1 0x1e
52
53/* PHY Control 2 / PHY Control (if no PHY Control 1) */
54#define MII_KSZPHY_CTRL_2 0x1f
55#define MII_KSZPHY_CTRL MII_KSZPHY_CTRL_2
Choi, David51f932c2010-06-28 15:23:41 +000056/* bitmap of PHY register to set interrupt mode */
Johan Hovold00aee092014-11-11 20:00:09 +010057#define KSZPHY_CTRL_INT_ACTIVE_HIGH BIT(9)
58#define KSZ9021_CTRL_INT_ACTIVE_HIGH BIT(14)
59#define KS8737_CTRL_INT_ACTIVE_HIGH BIT(14)
60#define KSZ8051_RMII_50MHZ_CLK BIT(7)
Choi, David51f932c2010-06-28 15:23:41 +000061
Sean Cross954c3962013-08-21 01:46:12 +000062/* Write/read to/from extended registers */
63#define MII_KSZPHY_EXTREG 0x0b
64#define KSZPHY_EXTREG_WRITE 0x8000
65
66#define MII_KSZPHY_EXTREG_WRITE 0x0c
67#define MII_KSZPHY_EXTREG_READ 0x0d
68
69/* Extended registers */
70#define MII_KSZPHY_CLK_CONTROL_PAD_SKEW 0x104
71#define MII_KSZPHY_RX_DATA_PAD_SKEW 0x105
72#define MII_KSZPHY_TX_DATA_PAD_SKEW 0x106
73
74#define PS_TO_REG 200
75
Johan Hovolde6a423a2014-11-19 12:59:15 +010076struct kszphy_type {
77 u32 led_mode_reg;
Johan Hovold0f959032014-11-19 12:59:17 +010078 bool has_broadcast_disable;
Johan Hovolde6a423a2014-11-19 12:59:15 +010079};
80
81struct kszphy_priv {
82 const struct kszphy_type *type;
Johan Hovolde7a792e2014-11-19 12:59:16 +010083 int led_mode;
Johan Hovolde6a423a2014-11-19 12:59:15 +010084};
85
86static const struct kszphy_type ksz8021_type = {
87 .led_mode_reg = MII_KSZPHY_CTRL_2,
88};
89
90static const struct kszphy_type ksz8041_type = {
91 .led_mode_reg = MII_KSZPHY_CTRL_1,
92};
93
94static const struct kszphy_type ksz8051_type = {
95 .led_mode_reg = MII_KSZPHY_CTRL_2,
96};
97
98static const struct kszphy_type ksz8081_type = {
99 .led_mode_reg = MII_KSZPHY_CTRL_2,
Johan Hovold0f959032014-11-19 12:59:17 +0100100 .has_broadcast_disable = true,
Johan Hovolde6a423a2014-11-19 12:59:15 +0100101};
102
Hector Palaciosb6bb4dfc2013-03-10 22:50:03 +0000103static int ksz_config_flags(struct phy_device *phydev)
104{
105 int regval;
106
Sascha Hauer1fadee02014-10-10 09:48:05 +0200107 if (phydev->dev_flags & (MICREL_PHY_50MHZ_CLK | MICREL_PHY_25MHZ_CLK)) {
Hector Palaciosb6bb4dfc2013-03-10 22:50:03 +0000108 regval = phy_read(phydev, MII_KSZPHY_CTRL);
Sascha Hauer1fadee02014-10-10 09:48:05 +0200109 if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK)
110 regval |= KSZ8051_RMII_50MHZ_CLK;
111 else
112 regval &= ~KSZ8051_RMII_50MHZ_CLK;
Hector Palaciosb6bb4dfc2013-03-10 22:50:03 +0000113 return phy_write(phydev, MII_KSZPHY_CTRL, regval);
114 }
115 return 0;
116}
117
Sean Cross954c3962013-08-21 01:46:12 +0000118static int kszphy_extended_write(struct phy_device *phydev,
Florian Fainelli756b5082013-12-17 21:38:11 -0800119 u32 regnum, u16 val)
Sean Cross954c3962013-08-21 01:46:12 +0000120{
121 phy_write(phydev, MII_KSZPHY_EXTREG, KSZPHY_EXTREG_WRITE | regnum);
122 return phy_write(phydev, MII_KSZPHY_EXTREG_WRITE, val);
123}
124
125static int kszphy_extended_read(struct phy_device *phydev,
Florian Fainelli756b5082013-12-17 21:38:11 -0800126 u32 regnum)
Sean Cross954c3962013-08-21 01:46:12 +0000127{
128 phy_write(phydev, MII_KSZPHY_EXTREG, regnum);
129 return phy_read(phydev, MII_KSZPHY_EXTREG_READ);
130}
131
Choi, David51f932c2010-06-28 15:23:41 +0000132static int kszphy_ack_interrupt(struct phy_device *phydev)
133{
134 /* bit[7..0] int status, which is a read and clear register. */
135 int rc;
136
137 rc = phy_read(phydev, MII_KSZPHY_INTCS);
138
139 return (rc < 0) ? rc : 0;
140}
141
142static int kszphy_set_interrupt(struct phy_device *phydev)
143{
144 int temp;
145 temp = (PHY_INTERRUPT_ENABLED == phydev->interrupts) ?
146 KSZPHY_INTCS_ALL : 0;
147 return phy_write(phydev, MII_KSZPHY_INTCS, temp);
148}
149
150static int kszphy_config_intr(struct phy_device *phydev)
151{
152 int temp, rc;
153
154 /* set the interrupt pin active low */
155 temp = phy_read(phydev, MII_KSZPHY_CTRL);
Johan Hovold5bb8fc02014-11-11 20:00:08 +0100156 if (temp < 0)
157 return temp;
Choi, David51f932c2010-06-28 15:23:41 +0000158 temp &= ~KSZPHY_CTRL_INT_ACTIVE_HIGH;
159 phy_write(phydev, MII_KSZPHY_CTRL, temp);
160 rc = kszphy_set_interrupt(phydev);
161 return rc < 0 ? rc : 0;
162}
163
164static int ksz9021_config_intr(struct phy_device *phydev)
165{
166 int temp, rc;
167
168 /* set the interrupt pin active low */
169 temp = phy_read(phydev, MII_KSZPHY_CTRL);
Johan Hovold5bb8fc02014-11-11 20:00:08 +0100170 if (temp < 0)
171 return temp;
Choi, David51f932c2010-06-28 15:23:41 +0000172 temp &= ~KSZ9021_CTRL_INT_ACTIVE_HIGH;
173 phy_write(phydev, MII_KSZPHY_CTRL, temp);
174 rc = kszphy_set_interrupt(phydev);
175 return rc < 0 ? rc : 0;
176}
177
178static int ks8737_config_intr(struct phy_device *phydev)
179{
180 int temp, rc;
181
182 /* set the interrupt pin active low */
183 temp = phy_read(phydev, MII_KSZPHY_CTRL);
Johan Hovold5bb8fc02014-11-11 20:00:08 +0100184 if (temp < 0)
185 return temp;
Choi, David51f932c2010-06-28 15:23:41 +0000186 temp &= ~KS8737_CTRL_INT_ACTIVE_HIGH;
187 phy_write(phydev, MII_KSZPHY_CTRL, temp);
188 rc = kszphy_set_interrupt(phydev);
189 return rc < 0 ? rc : 0;
190}
David J. Choid0507002010-04-29 06:12:41 +0000191
Johan Hovolde7a792e2014-11-19 12:59:16 +0100192static int kszphy_setup_led(struct phy_device *phydev, u32 reg, int val)
Ben Dooks20d84352014-02-26 11:48:00 +0000193{
Johan Hovold5a167782014-11-11 20:00:14 +0100194 int rc, temp, shift;
Johan Hovold86205462014-11-11 20:00:12 +0100195
Johan Hovold5a167782014-11-11 20:00:14 +0100196 switch (reg) {
197 case MII_KSZPHY_CTRL_1:
198 shift = 14;
199 break;
200 case MII_KSZPHY_CTRL_2:
201 shift = 4;
202 break;
203 default:
204 return -EINVAL;
205 }
206
Ben Dooks20d84352014-02-26 11:48:00 +0000207 temp = phy_read(phydev, reg);
Johan Hovoldb7035862014-11-11 20:00:13 +0100208 if (temp < 0) {
209 rc = temp;
210 goto out;
211 }
Ben Dooks20d84352014-02-26 11:48:00 +0000212
Sergei Shtylyov28bdc492014-03-19 02:58:16 +0300213 temp &= ~(3 << shift);
Ben Dooks20d84352014-02-26 11:48:00 +0000214 temp |= val << shift;
215 rc = phy_write(phydev, reg, temp);
Johan Hovoldb7035862014-11-11 20:00:13 +0100216out:
217 if (rc < 0)
218 dev_err(&phydev->dev, "failed to set led mode\n");
Ben Dooks20d84352014-02-26 11:48:00 +0000219
Johan Hovoldb7035862014-11-11 20:00:13 +0100220 return rc;
Ben Dooks20d84352014-02-26 11:48:00 +0000221}
222
Johan Hovoldbde15122014-11-11 20:00:10 +0100223/* Disable PHY address 0 as the broadcast address, so that it can be used as a
224 * unique (non-broadcast) address on a shared bus.
225 */
226static int kszphy_broadcast_disable(struct phy_device *phydev)
227{
228 int ret;
229
230 ret = phy_read(phydev, MII_KSZPHY_OMSO);
231 if (ret < 0)
232 goto out;
233
234 ret = phy_write(phydev, MII_KSZPHY_OMSO, ret | KSZPHY_OMSO_B_CAST_OFF);
235out:
236 if (ret)
237 dev_err(&phydev->dev, "failed to disable broadcast address\n");
238
239 return ret;
240}
241
David J. Choid0507002010-04-29 06:12:41 +0000242static int kszphy_config_init(struct phy_device *phydev)
243{
Johan Hovolde6a423a2014-11-19 12:59:15 +0100244 struct kszphy_priv *priv = phydev->priv;
245 const struct kszphy_type *type;
David J. Choid0507002010-04-29 06:12:41 +0000246
Johan Hovolde6a423a2014-11-19 12:59:15 +0100247 if (!priv)
248 return 0;
249
250 type = priv->type;
251
Johan Hovold0f959032014-11-19 12:59:17 +0100252 if (type->has_broadcast_disable)
253 kszphy_broadcast_disable(phydev);
254
Johan Hovolde7a792e2014-11-19 12:59:16 +0100255 if (priv->led_mode >= 0)
256 kszphy_setup_led(phydev, type->led_mode_reg, priv->led_mode);
Johan Hovolde6a423a2014-11-19 12:59:15 +0100257
258 return 0;
Ben Dooks20d84352014-02-26 11:48:00 +0000259}
260
Marek Vasut212ea992012-09-23 16:58:49 +0000261static int ksz8021_config_init(struct phy_device *phydev)
262{
Ben Dooks20d84352014-02-26 11:48:00 +0000263 int rc;
264
Johan Hovolde6a423a2014-11-19 12:59:15 +0100265 kszphy_config_init(phydev);
Ben Dooks20d84352014-02-26 11:48:00 +0000266
Hector Palaciosb6bb4dfc2013-03-10 22:50:03 +0000267 rc = ksz_config_flags(phydev);
Bruno Thomsenb838b4a2014-10-09 16:48:14 +0200268 if (rc < 0)
269 return rc;
Johan Hovoldbde15122014-11-11 20:00:10 +0100270
271 rc = kszphy_broadcast_disable(phydev);
272
Hector Palaciosb6bb4dfc2013-03-10 22:50:03 +0000273 return rc < 0 ? rc : 0;
Marek Vasut212ea992012-09-23 16:58:49 +0000274}
275
Baruch Siachd606ef32011-02-14 02:05:33 +0000276static int ks8051_config_init(struct phy_device *phydev)
277{
Hector Palaciosb6bb4dfc2013-03-10 22:50:03 +0000278 int rc;
Baruch Siachd606ef32011-02-14 02:05:33 +0000279
Johan Hovolde6a423a2014-11-19 12:59:15 +0100280 kszphy_config_init(phydev);
Ben Dooks20d84352014-02-26 11:48:00 +0000281
Hector Palaciosb6bb4dfc2013-03-10 22:50:03 +0000282 rc = ksz_config_flags(phydev);
283 return rc < 0 ? rc : 0;
Baruch Siachd606ef32011-02-14 02:05:33 +0000284}
285
Sean Cross954c3962013-08-21 01:46:12 +0000286static int ksz9021_load_values_from_of(struct phy_device *phydev,
287 struct device_node *of_node, u16 reg,
288 char *field1, char *field2,
289 char *field3, char *field4)
290{
291 int val1 = -1;
292 int val2 = -2;
293 int val3 = -3;
294 int val4 = -4;
295 int newval;
296 int matches = 0;
297
298 if (!of_property_read_u32(of_node, field1, &val1))
299 matches++;
300
301 if (!of_property_read_u32(of_node, field2, &val2))
302 matches++;
303
304 if (!of_property_read_u32(of_node, field3, &val3))
305 matches++;
306
307 if (!of_property_read_u32(of_node, field4, &val4))
308 matches++;
309
310 if (!matches)
311 return 0;
312
313 if (matches < 4)
314 newval = kszphy_extended_read(phydev, reg);
315 else
316 newval = 0;
317
318 if (val1 != -1)
319 newval = ((newval & 0xfff0) | ((val1 / PS_TO_REG) & 0xf) << 0);
320
Hubert Chaumette6a119742014-04-22 15:01:04 +0200321 if (val2 != -2)
Sean Cross954c3962013-08-21 01:46:12 +0000322 newval = ((newval & 0xff0f) | ((val2 / PS_TO_REG) & 0xf) << 4);
323
Hubert Chaumette6a119742014-04-22 15:01:04 +0200324 if (val3 != -3)
Sean Cross954c3962013-08-21 01:46:12 +0000325 newval = ((newval & 0xf0ff) | ((val3 / PS_TO_REG) & 0xf) << 8);
326
Hubert Chaumette6a119742014-04-22 15:01:04 +0200327 if (val4 != -4)
Sean Cross954c3962013-08-21 01:46:12 +0000328 newval = ((newval & 0x0fff) | ((val4 / PS_TO_REG) & 0xf) << 12);
329
330 return kszphy_extended_write(phydev, reg, newval);
331}
332
333static int ksz9021_config_init(struct phy_device *phydev)
334{
335 struct device *dev = &phydev->dev;
336 struct device_node *of_node = dev->of_node;
337
338 if (!of_node && dev->parent->of_node)
339 of_node = dev->parent->of_node;
340
341 if (of_node) {
342 ksz9021_load_values_from_of(phydev, of_node,
343 MII_KSZPHY_CLK_CONTROL_PAD_SKEW,
344 "txen-skew-ps", "txc-skew-ps",
345 "rxdv-skew-ps", "rxc-skew-ps");
346 ksz9021_load_values_from_of(phydev, of_node,
347 MII_KSZPHY_RX_DATA_PAD_SKEW,
348 "rxd0-skew-ps", "rxd1-skew-ps",
349 "rxd2-skew-ps", "rxd3-skew-ps");
350 ksz9021_load_values_from_of(phydev, of_node,
351 MII_KSZPHY_TX_DATA_PAD_SKEW,
352 "txd0-skew-ps", "txd1-skew-ps",
353 "txd2-skew-ps", "txd3-skew-ps");
354 }
355 return 0;
356}
357
Hubert Chaumette6e4b8272014-05-06 09:40:17 +0200358#define MII_KSZ9031RN_MMD_CTRL_REG 0x0d
359#define MII_KSZ9031RN_MMD_REGDATA_REG 0x0e
360#define OP_DATA 1
361#define KSZ9031_PS_TO_REG 60
362
363/* Extended registers */
364#define MII_KSZ9031RN_CONTROL_PAD_SKEW 4
365#define MII_KSZ9031RN_RX_DATA_PAD_SKEW 5
366#define MII_KSZ9031RN_TX_DATA_PAD_SKEW 6
367#define MII_KSZ9031RN_CLK_PAD_SKEW 8
368
369static int ksz9031_extended_write(struct phy_device *phydev,
370 u8 mode, u32 dev_addr, u32 regnum, u16 val)
371{
372 phy_write(phydev, MII_KSZ9031RN_MMD_CTRL_REG, dev_addr);
373 phy_write(phydev, MII_KSZ9031RN_MMD_REGDATA_REG, regnum);
374 phy_write(phydev, MII_KSZ9031RN_MMD_CTRL_REG, (mode << 14) | dev_addr);
375 return phy_write(phydev, MII_KSZ9031RN_MMD_REGDATA_REG, val);
376}
377
378static int ksz9031_extended_read(struct phy_device *phydev,
379 u8 mode, u32 dev_addr, u32 regnum)
380{
381 phy_write(phydev, MII_KSZ9031RN_MMD_CTRL_REG, dev_addr);
382 phy_write(phydev, MII_KSZ9031RN_MMD_REGDATA_REG, regnum);
383 phy_write(phydev, MII_KSZ9031RN_MMD_CTRL_REG, (mode << 14) | dev_addr);
384 return phy_read(phydev, MII_KSZ9031RN_MMD_REGDATA_REG);
385}
386
387static int ksz9031_of_load_skew_values(struct phy_device *phydev,
388 struct device_node *of_node,
389 u16 reg, size_t field_sz,
390 char *field[], u8 numfields)
391{
392 int val[4] = {-1, -2, -3, -4};
393 int matches = 0;
394 u16 mask;
395 u16 maxval;
396 u16 newval;
397 int i;
398
399 for (i = 0; i < numfields; i++)
400 if (!of_property_read_u32(of_node, field[i], val + i))
401 matches++;
402
403 if (!matches)
404 return 0;
405
406 if (matches < numfields)
407 newval = ksz9031_extended_read(phydev, OP_DATA, 2, reg);
408 else
409 newval = 0;
410
411 maxval = (field_sz == 4) ? 0xf : 0x1f;
412 for (i = 0; i < numfields; i++)
413 if (val[i] != -(i + 1)) {
414 mask = 0xffff;
415 mask ^= maxval << (field_sz * i);
416 newval = (newval & mask) |
417 (((val[i] / KSZ9031_PS_TO_REG) & maxval)
418 << (field_sz * i));
419 }
420
421 return ksz9031_extended_write(phydev, OP_DATA, 2, reg, newval);
422}
423
424static int ksz9031_config_init(struct phy_device *phydev)
425{
426 struct device *dev = &phydev->dev;
427 struct device_node *of_node = dev->of_node;
428 char *clk_skews[2] = {"rxc-skew-ps", "txc-skew-ps"};
429 char *rx_data_skews[4] = {
430 "rxd0-skew-ps", "rxd1-skew-ps",
431 "rxd2-skew-ps", "rxd3-skew-ps"
432 };
433 char *tx_data_skews[4] = {
434 "txd0-skew-ps", "txd1-skew-ps",
435 "txd2-skew-ps", "txd3-skew-ps"
436 };
437 char *control_skews[2] = {"txen-skew-ps", "rxdv-skew-ps"};
438
439 if (!of_node && dev->parent->of_node)
440 of_node = dev->parent->of_node;
441
442 if (of_node) {
443 ksz9031_of_load_skew_values(phydev, of_node,
444 MII_KSZ9031RN_CLK_PAD_SKEW, 5,
445 clk_skews, 2);
446
447 ksz9031_of_load_skew_values(phydev, of_node,
448 MII_KSZ9031RN_CONTROL_PAD_SKEW, 4,
449 control_skews, 2);
450
451 ksz9031_of_load_skew_values(phydev, of_node,
452 MII_KSZ9031RN_RX_DATA_PAD_SKEW, 4,
453 rx_data_skews, 4);
454
455 ksz9031_of_load_skew_values(phydev, of_node,
456 MII_KSZ9031RN_TX_DATA_PAD_SKEW, 4,
457 tx_data_skews, 4);
458 }
459 return 0;
460}
461
Jean-Christophe PLAGNIOL-VILLARD93272e02012-11-21 05:38:07 +0000462#define KSZ8873MLL_GLOBAL_CONTROL_4 0x06
Johan Hovold00aee092014-11-11 20:00:09 +0100463#define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX BIT(6)
464#define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED BIT(4)
Jingoo Han32d73b12013-08-06 17:29:35 +0900465static int ksz8873mll_read_status(struct phy_device *phydev)
Jean-Christophe PLAGNIOL-VILLARD93272e02012-11-21 05:38:07 +0000466{
467 int regval;
468
469 /* dummy read */
470 regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);
471
472 regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);
473
474 if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX)
475 phydev->duplex = DUPLEX_HALF;
476 else
477 phydev->duplex = DUPLEX_FULL;
478
479 if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_SPEED)
480 phydev->speed = SPEED_10;
481 else
482 phydev->speed = SPEED_100;
483
484 phydev->link = 1;
485 phydev->pause = phydev->asym_pause = 0;
486
487 return 0;
488}
489
490static int ksz8873mll_config_aneg(struct phy_device *phydev)
491{
492 return 0;
493}
494
Vince Bridgers19936942014-07-29 15:19:58 -0500495/* This routine returns -1 as an indication to the caller that the
496 * Micrel ksz9021 10/100/1000 PHY does not support standard IEEE
497 * MMD extended PHY registers.
498 */
499static int
500ksz9021_rd_mmd_phyreg(struct phy_device *phydev, int ptrad, int devnum,
501 int regnum)
502{
503 return -1;
504}
505
506/* This routine does nothing since the Micrel ksz9021 does not support
507 * standard IEEE MMD extended PHY registers.
508 */
509static void
510ksz9021_wr_mmd_phyreg(struct phy_device *phydev, int ptrad, int devnum,
511 int regnum, u32 val)
512{
513}
514
Johan Hovolde6a423a2014-11-19 12:59:15 +0100515static int kszphy_probe(struct phy_device *phydev)
516{
517 const struct kszphy_type *type = phydev->drv->driver_data;
Johan Hovolde7a792e2014-11-19 12:59:16 +0100518 struct device_node *np = phydev->dev.of_node;
Johan Hovolde6a423a2014-11-19 12:59:15 +0100519 struct kszphy_priv *priv;
Johan Hovolde7a792e2014-11-19 12:59:16 +0100520 int ret;
Johan Hovolde6a423a2014-11-19 12:59:15 +0100521
522 priv = devm_kzalloc(&phydev->dev, sizeof(*priv), GFP_KERNEL);
523 if (!priv)
524 return -ENOMEM;
525
526 phydev->priv = priv;
527
528 priv->type = type;
529
Johan Hovolde7a792e2014-11-19 12:59:16 +0100530 if (type->led_mode_reg) {
531 ret = of_property_read_u32(np, "micrel,led-mode",
532 &priv->led_mode);
533 if (ret)
534 priv->led_mode = -1;
535
536 if (priv->led_mode > 3) {
537 dev_err(&phydev->dev, "invalid led mode: 0x%02x\n",
538 priv->led_mode);
539 priv->led_mode = -1;
540 }
541 } else {
542 priv->led_mode = -1;
543 }
544
Johan Hovolde6a423a2014-11-19 12:59:15 +0100545 return 0;
546}
547
Sascha Hauer1fadee02014-10-10 09:48:05 +0200548static int ksz8021_probe(struct phy_device *phydev)
549{
550 struct clk *clk;
551
552 clk = devm_clk_get(&phydev->dev, "rmii-ref");
553 if (!IS_ERR(clk)) {
554 unsigned long rate = clk_get_rate(clk);
555
556 if (rate > 24500000 && rate < 25500000) {
557 phydev->dev_flags |= MICREL_PHY_25MHZ_CLK;
558 } else if (rate > 49500000 && rate < 50500000) {
559 phydev->dev_flags |= MICREL_PHY_50MHZ_CLK;
560 } else {
561 dev_err(&phydev->dev, "Clock rate out of range: %ld\n", rate);
562 return -EINVAL;
563 }
564 }
565
Johan Hovolde6a423a2014-11-19 12:59:15 +0100566 return kszphy_probe(phydev);
Sascha Hauer1fadee02014-10-10 09:48:05 +0200567}
568
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000569static struct phy_driver ksphy_driver[] = {
570{
Choi, David51f932c2010-06-28 15:23:41 +0000571 .phy_id = PHY_ID_KS8737,
David J. Choid0507002010-04-29 06:12:41 +0000572 .phy_id_mask = 0x00fffff0,
Choi, David51f932c2010-06-28 15:23:41 +0000573 .name = "Micrel KS8737",
574 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
575 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
David J. Choid0507002010-04-29 06:12:41 +0000576 .config_init = kszphy_config_init,
577 .config_aneg = genphy_config_aneg,
578 .read_status = genphy_read_status,
Choi, David51f932c2010-06-28 15:23:41 +0000579 .ack_interrupt = kszphy_ack_interrupt,
580 .config_intr = ks8737_config_intr,
Patrice Vilchez1a5465f2013-09-19 19:40:48 +0200581 .suspend = genphy_suspend,
582 .resume = genphy_resume,
David J. Choid0507002010-04-29 06:12:41 +0000583 .driver = { .owner = THIS_MODULE,},
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000584}, {
Marek Vasut212ea992012-09-23 16:58:49 +0000585 .phy_id = PHY_ID_KSZ8021,
586 .phy_id_mask = 0x00ffffff,
David J. Choi7ab59dc2013-01-23 14:05:15 +0000587 .name = "Micrel KSZ8021 or KSZ8031",
Marek Vasut212ea992012-09-23 16:58:49 +0000588 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause |
589 SUPPORTED_Asym_Pause),
590 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
Johan Hovolde6a423a2014-11-19 12:59:15 +0100591 .driver_data = &ksz8021_type,
Sascha Hauer1fadee02014-10-10 09:48:05 +0200592 .probe = ksz8021_probe,
Marek Vasut212ea992012-09-23 16:58:49 +0000593 .config_init = ksz8021_config_init,
594 .config_aneg = genphy_config_aneg,
595 .read_status = genphy_read_status,
596 .ack_interrupt = kszphy_ack_interrupt,
597 .config_intr = kszphy_config_intr,
Patrice Vilchez1a5465f2013-09-19 19:40:48 +0200598 .suspend = genphy_suspend,
599 .resume = genphy_resume,
Marek Vasut212ea992012-09-23 16:58:49 +0000600 .driver = { .owner = THIS_MODULE,},
601}, {
Hector Palaciosb818d1a2013-03-10 22:50:02 +0000602 .phy_id = PHY_ID_KSZ8031,
603 .phy_id_mask = 0x00ffffff,
604 .name = "Micrel KSZ8031",
605 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause |
606 SUPPORTED_Asym_Pause),
607 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
Johan Hovolde6a423a2014-11-19 12:59:15 +0100608 .driver_data = &ksz8021_type,
Sascha Hauer1fadee02014-10-10 09:48:05 +0200609 .probe = ksz8021_probe,
Hector Palaciosb818d1a2013-03-10 22:50:02 +0000610 .config_init = ksz8021_config_init,
611 .config_aneg = genphy_config_aneg,
612 .read_status = genphy_read_status,
613 .ack_interrupt = kszphy_ack_interrupt,
614 .config_intr = kszphy_config_intr,
Patrice Vilchez1a5465f2013-09-19 19:40:48 +0200615 .suspend = genphy_suspend,
616 .resume = genphy_resume,
Hector Palaciosb818d1a2013-03-10 22:50:02 +0000617 .driver = { .owner = THIS_MODULE,},
618}, {
Marek Vasut510d5732012-09-23 16:58:50 +0000619 .phy_id = PHY_ID_KSZ8041,
David J. Choid0507002010-04-29 06:12:41 +0000620 .phy_id_mask = 0x00fffff0,
Marek Vasut510d5732012-09-23 16:58:50 +0000621 .name = "Micrel KSZ8041",
Choi, David51f932c2010-06-28 15:23:41 +0000622 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
623 | SUPPORTED_Asym_Pause),
624 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
Johan Hovolde6a423a2014-11-19 12:59:15 +0100625 .driver_data = &ksz8041_type,
626 .probe = kszphy_probe,
627 .config_init = kszphy_config_init,
David J. Choid0507002010-04-29 06:12:41 +0000628 .config_aneg = genphy_config_aneg,
629 .read_status = genphy_read_status,
Choi, David51f932c2010-06-28 15:23:41 +0000630 .ack_interrupt = kszphy_ack_interrupt,
631 .config_intr = kszphy_config_intr,
Patrice Vilchez1a5465f2013-09-19 19:40:48 +0200632 .suspend = genphy_suspend,
633 .resume = genphy_resume,
Choi, David51f932c2010-06-28 15:23:41 +0000634 .driver = { .owner = THIS_MODULE,},
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000635}, {
Sergei Shtylyov4bd7b512013-12-10 02:20:41 +0300636 .phy_id = PHY_ID_KSZ8041RNLI,
637 .phy_id_mask = 0x00fffff0,
638 .name = "Micrel KSZ8041RNLI",
639 .features = PHY_BASIC_FEATURES |
640 SUPPORTED_Pause | SUPPORTED_Asym_Pause,
641 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
Johan Hovolde6a423a2014-11-19 12:59:15 +0100642 .driver_data = &ksz8041_type,
643 .probe = kszphy_probe,
644 .config_init = kszphy_config_init,
Sergei Shtylyov4bd7b512013-12-10 02:20:41 +0300645 .config_aneg = genphy_config_aneg,
646 .read_status = genphy_read_status,
647 .ack_interrupt = kszphy_ack_interrupt,
648 .config_intr = kszphy_config_intr,
649 .suspend = genphy_suspend,
650 .resume = genphy_resume,
651 .driver = { .owner = THIS_MODULE,},
652}, {
Marek Vasut510d5732012-09-23 16:58:50 +0000653 .phy_id = PHY_ID_KSZ8051,
Choi, David51f932c2010-06-28 15:23:41 +0000654 .phy_id_mask = 0x00fffff0,
Marek Vasut510d5732012-09-23 16:58:50 +0000655 .name = "Micrel KSZ8051",
Choi, David51f932c2010-06-28 15:23:41 +0000656 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
657 | SUPPORTED_Asym_Pause),
658 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
Johan Hovolde6a423a2014-11-19 12:59:15 +0100659 .driver_data = &ksz8051_type,
660 .probe = kszphy_probe,
Baruch Siachd606ef32011-02-14 02:05:33 +0000661 .config_init = ks8051_config_init,
Choi, David51f932c2010-06-28 15:23:41 +0000662 .config_aneg = genphy_config_aneg,
663 .read_status = genphy_read_status,
664 .ack_interrupt = kszphy_ack_interrupt,
665 .config_intr = kszphy_config_intr,
Patrice Vilchez1a5465f2013-09-19 19:40:48 +0200666 .suspend = genphy_suspend,
667 .resume = genphy_resume,
Choi, David51f932c2010-06-28 15:23:41 +0000668 .driver = { .owner = THIS_MODULE,},
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000669}, {
Marek Vasut510d5732012-09-23 16:58:50 +0000670 .phy_id = PHY_ID_KSZ8001,
671 .name = "Micrel KSZ8001 or KS8721",
Jason Wang48d7d0a2012-06-17 22:52:09 +0000672 .phy_id_mask = 0x00ffffff,
Choi, David51f932c2010-06-28 15:23:41 +0000673 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
674 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
Johan Hovolde6a423a2014-11-19 12:59:15 +0100675 .driver_data = &ksz8041_type,
676 .probe = kszphy_probe,
677 .config_init = kszphy_config_init,
Choi, David51f932c2010-06-28 15:23:41 +0000678 .config_aneg = genphy_config_aneg,
679 .read_status = genphy_read_status,
680 .ack_interrupt = kszphy_ack_interrupt,
681 .config_intr = kszphy_config_intr,
Patrice Vilchez1a5465f2013-09-19 19:40:48 +0200682 .suspend = genphy_suspend,
683 .resume = genphy_resume,
David J. Choid0507002010-04-29 06:12:41 +0000684 .driver = { .owner = THIS_MODULE,},
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000685}, {
David J. Choi7ab59dc2013-01-23 14:05:15 +0000686 .phy_id = PHY_ID_KSZ8081,
687 .name = "Micrel KSZ8081 or KSZ8091",
688 .phy_id_mask = 0x00fffff0,
689 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
690 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
Johan Hovolde6a423a2014-11-19 12:59:15 +0100691 .driver_data = &ksz8081_type,
692 .probe = kszphy_probe,
Johan Hovold0f959032014-11-19 12:59:17 +0100693 .config_init = kszphy_config_init,
David J. Choi7ab59dc2013-01-23 14:05:15 +0000694 .config_aneg = genphy_config_aneg,
695 .read_status = genphy_read_status,
696 .ack_interrupt = kszphy_ack_interrupt,
697 .config_intr = kszphy_config_intr,
Patrice Vilchez1a5465f2013-09-19 19:40:48 +0200698 .suspend = genphy_suspend,
699 .resume = genphy_resume,
David J. Choi7ab59dc2013-01-23 14:05:15 +0000700 .driver = { .owner = THIS_MODULE,},
701}, {
702 .phy_id = PHY_ID_KSZ8061,
703 .name = "Micrel KSZ8061",
704 .phy_id_mask = 0x00fffff0,
705 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
706 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
707 .config_init = kszphy_config_init,
708 .config_aneg = genphy_config_aneg,
709 .read_status = genphy_read_status,
710 .ack_interrupt = kszphy_ack_interrupt,
711 .config_intr = kszphy_config_intr,
Patrice Vilchez1a5465f2013-09-19 19:40:48 +0200712 .suspend = genphy_suspend,
713 .resume = genphy_resume,
David J. Choi7ab59dc2013-01-23 14:05:15 +0000714 .driver = { .owner = THIS_MODULE,},
715}, {
David J. Choid0507002010-04-29 06:12:41 +0000716 .phy_id = PHY_ID_KSZ9021,
Jason Wang48d7d0a2012-06-17 22:52:09 +0000717 .phy_id_mask = 0x000ffffe,
David J. Choid0507002010-04-29 06:12:41 +0000718 .name = "Micrel KSZ9021 Gigabit PHY",
Vlastimil Kosar32fcafb2013-02-28 08:45:22 +0000719 .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause),
Choi, David51f932c2010-06-28 15:23:41 +0000720 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
Sean Cross954c3962013-08-21 01:46:12 +0000721 .config_init = ksz9021_config_init,
David J. Choid0507002010-04-29 06:12:41 +0000722 .config_aneg = genphy_config_aneg,
723 .read_status = genphy_read_status,
Choi, David51f932c2010-06-28 15:23:41 +0000724 .ack_interrupt = kszphy_ack_interrupt,
725 .config_intr = ksz9021_config_intr,
Patrice Vilchez1a5465f2013-09-19 19:40:48 +0200726 .suspend = genphy_suspend,
727 .resume = genphy_resume,
Vince Bridgers19936942014-07-29 15:19:58 -0500728 .read_mmd_indirect = ksz9021_rd_mmd_phyreg,
729 .write_mmd_indirect = ksz9021_wr_mmd_phyreg,
David J. Choid0507002010-04-29 06:12:41 +0000730 .driver = { .owner = THIS_MODULE, },
Jean-Christophe PLAGNIOL-VILLARD93272e02012-11-21 05:38:07 +0000731}, {
David J. Choi7ab59dc2013-01-23 14:05:15 +0000732 .phy_id = PHY_ID_KSZ9031,
733 .phy_id_mask = 0x00fffff0,
734 .name = "Micrel KSZ9031 Gigabit PHY",
Mike Looijmans95e8b102014-09-15 12:06:33 +0200735 .features = (PHY_GBIT_FEATURES | SUPPORTED_Pause),
David J. Choi7ab59dc2013-01-23 14:05:15 +0000736 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
Hubert Chaumette6e4b8272014-05-06 09:40:17 +0200737 .config_init = ksz9031_config_init,
David J. Choi7ab59dc2013-01-23 14:05:15 +0000738 .config_aneg = genphy_config_aneg,
739 .read_status = genphy_read_status,
740 .ack_interrupt = kszphy_ack_interrupt,
741 .config_intr = ksz9021_config_intr,
Patrice Vilchez1a5465f2013-09-19 19:40:48 +0200742 .suspend = genphy_suspend,
743 .resume = genphy_resume,
David J. Choi7ab59dc2013-01-23 14:05:15 +0000744 .driver = { .owner = THIS_MODULE, },
745}, {
Jean-Christophe PLAGNIOL-VILLARD93272e02012-11-21 05:38:07 +0000746 .phy_id = PHY_ID_KSZ8873MLL,
747 .phy_id_mask = 0x00fffff0,
748 .name = "Micrel KSZ8873MLL Switch",
749 .features = (SUPPORTED_Pause | SUPPORTED_Asym_Pause),
750 .flags = PHY_HAS_MAGICANEG,
751 .config_init = kszphy_config_init,
752 .config_aneg = ksz8873mll_config_aneg,
753 .read_status = ksz8873mll_read_status,
Patrice Vilchez1a5465f2013-09-19 19:40:48 +0200754 .suspend = genphy_suspend,
755 .resume = genphy_resume,
Jean-Christophe PLAGNIOL-VILLARD93272e02012-11-21 05:38:07 +0000756 .driver = { .owner = THIS_MODULE, },
David J. Choi7ab59dc2013-01-23 14:05:15 +0000757}, {
758 .phy_id = PHY_ID_KSZ886X,
759 .phy_id_mask = 0x00fffff0,
760 .name = "Micrel KSZ886X Switch",
761 .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
762 .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
763 .config_init = kszphy_config_init,
764 .config_aneg = genphy_config_aneg,
765 .read_status = genphy_read_status,
Patrice Vilchez1a5465f2013-09-19 19:40:48 +0200766 .suspend = genphy_suspend,
767 .resume = genphy_resume,
David J. Choi7ab59dc2013-01-23 14:05:15 +0000768 .driver = { .owner = THIS_MODULE, },
Christian Hohnstaedtd5bf9072012-07-04 05:44:34 +0000769} };
David J. Choid0507002010-04-29 06:12:41 +0000770
Johan Hovold50fd7152014-11-11 19:45:59 +0100771module_phy_driver(ksphy_driver);
David J. Choid0507002010-04-29 06:12:41 +0000772
773MODULE_DESCRIPTION("Micrel PHY driver");
774MODULE_AUTHOR("David J. Choi");
775MODULE_LICENSE("GPL");
David S. Miller52a60ed2010-05-03 15:48:29 -0700776
Uwe Kleine-Königcf93c942010-10-03 23:43:32 +0000777static struct mdio_device_id __maybe_unused micrel_tbl[] = {
Jason Wang48d7d0a2012-06-17 22:52:09 +0000778 { PHY_ID_KSZ9021, 0x000ffffe },
David J. Choi7ab59dc2013-01-23 14:05:15 +0000779 { PHY_ID_KSZ9031, 0x00fffff0 },
Marek Vasut510d5732012-09-23 16:58:50 +0000780 { PHY_ID_KSZ8001, 0x00ffffff },
Choi, David51f932c2010-06-28 15:23:41 +0000781 { PHY_ID_KS8737, 0x00fffff0 },
Marek Vasut212ea992012-09-23 16:58:49 +0000782 { PHY_ID_KSZ8021, 0x00ffffff },
Hector Palaciosb818d1a2013-03-10 22:50:02 +0000783 { PHY_ID_KSZ8031, 0x00ffffff },
Marek Vasut510d5732012-09-23 16:58:50 +0000784 { PHY_ID_KSZ8041, 0x00fffff0 },
785 { PHY_ID_KSZ8051, 0x00fffff0 },
David J. Choi7ab59dc2013-01-23 14:05:15 +0000786 { PHY_ID_KSZ8061, 0x00fffff0 },
787 { PHY_ID_KSZ8081, 0x00fffff0 },
Jean-Christophe PLAGNIOL-VILLARD93272e02012-11-21 05:38:07 +0000788 { PHY_ID_KSZ8873MLL, 0x00fffff0 },
David J. Choi7ab59dc2013-01-23 14:05:15 +0000789 { PHY_ID_KSZ886X, 0x00fffff0 },
David S. Miller52a60ed2010-05-03 15:48:29 -0700790 { }
791};
792
793MODULE_DEVICE_TABLE(mdio, micrel_tbl);