Linux 3.9-rc8
[firefly-linux-kernel-4.4.55.git] / drivers / net / phy / micrel.c
1 /*
2  * drivers/net/phy/micrel.c
3  *
4  * Driver for Micrel PHYs
5  *
6  * Author: David J. Choi
7  *
8  * Copyright (c) 2010-2013 Micrel, Inc.
9  *
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  *
15  * 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
22  */
23
24 #include <linux/kernel.h>
25 #include <linux/module.h>
26 #include <linux/phy.h>
27 #include <linux/micrel_phy.h>
28
29 /* Operation Mode Strap Override */
30 #define MII_KSZPHY_OMSO                         0x16
31 #define KSZPHY_OMSO_B_CAST_OFF                  (1 << 9)
32 #define KSZPHY_OMSO_RMII_OVERRIDE               (1 << 1)
33 #define KSZPHY_OMSO_MII_OVERRIDE                (1 << 0)
34
35 /* general Interrupt control/status reg in vendor specific block. */
36 #define MII_KSZPHY_INTCS                        0x1B
37 #define KSZPHY_INTCS_JABBER                     (1 << 15)
38 #define KSZPHY_INTCS_RECEIVE_ERR                (1 << 14)
39 #define KSZPHY_INTCS_PAGE_RECEIVE               (1 << 13)
40 #define KSZPHY_INTCS_PARELLEL                   (1 << 12)
41 #define KSZPHY_INTCS_LINK_PARTNER_ACK           (1 << 11)
42 #define KSZPHY_INTCS_LINK_DOWN                  (1 << 10)
43 #define KSZPHY_INTCS_REMOTE_FAULT               (1 << 9)
44 #define KSZPHY_INTCS_LINK_UP                    (1 << 8)
45 #define KSZPHY_INTCS_ALL                        (KSZPHY_INTCS_LINK_UP |\
46                                                 KSZPHY_INTCS_LINK_DOWN)
47
48 /* general PHY control reg in vendor specific block. */
49 #define MII_KSZPHY_CTRL                 0x1F
50 /* bitmap of PHY register to set interrupt mode */
51 #define KSZPHY_CTRL_INT_ACTIVE_HIGH             (1 << 9)
52 #define KSZ9021_CTRL_INT_ACTIVE_HIGH            (1 << 14)
53 #define KS8737_CTRL_INT_ACTIVE_HIGH             (1 << 14)
54 #define KSZ8051_RMII_50MHZ_CLK                  (1 << 7)
55
56 static int kszphy_ack_interrupt(struct phy_device *phydev)
57 {
58         /* bit[7..0] int status, which is a read and clear register. */
59         int rc;
60
61         rc = phy_read(phydev, MII_KSZPHY_INTCS);
62
63         return (rc < 0) ? rc : 0;
64 }
65
66 static int kszphy_set_interrupt(struct phy_device *phydev)
67 {
68         int temp;
69         temp = (PHY_INTERRUPT_ENABLED == phydev->interrupts) ?
70                 KSZPHY_INTCS_ALL : 0;
71         return phy_write(phydev, MII_KSZPHY_INTCS, temp);
72 }
73
74 static int kszphy_config_intr(struct phy_device *phydev)
75 {
76         int temp, rc;
77
78         /* set the interrupt pin active low */
79         temp = phy_read(phydev, MII_KSZPHY_CTRL);
80         temp &= ~KSZPHY_CTRL_INT_ACTIVE_HIGH;
81         phy_write(phydev, MII_KSZPHY_CTRL, temp);
82         rc = kszphy_set_interrupt(phydev);
83         return rc < 0 ? rc : 0;
84 }
85
86 static int ksz9021_config_intr(struct phy_device *phydev)
87 {
88         int temp, rc;
89
90         /* set the interrupt pin active low */
91         temp = phy_read(phydev, MII_KSZPHY_CTRL);
92         temp &= ~KSZ9021_CTRL_INT_ACTIVE_HIGH;
93         phy_write(phydev, MII_KSZPHY_CTRL, temp);
94         rc = kszphy_set_interrupt(phydev);
95         return rc < 0 ? rc : 0;
96 }
97
98 static int ks8737_config_intr(struct phy_device *phydev)
99 {
100         int temp, rc;
101
102         /* set the interrupt pin active low */
103         temp = phy_read(phydev, MII_KSZPHY_CTRL);
104         temp &= ~KS8737_CTRL_INT_ACTIVE_HIGH;
105         phy_write(phydev, MII_KSZPHY_CTRL, temp);
106         rc = kszphy_set_interrupt(phydev);
107         return rc < 0 ? rc : 0;
108 }
109
110 static int kszphy_config_init(struct phy_device *phydev)
111 {
112         return 0;
113 }
114
115 static int ksz8021_config_init(struct phy_device *phydev)
116 {
117         const u16 val = KSZPHY_OMSO_B_CAST_OFF | KSZPHY_OMSO_RMII_OVERRIDE;
118         phy_write(phydev, MII_KSZPHY_OMSO, val);
119         return 0;
120 }
121
122 static int ks8051_config_init(struct phy_device *phydev)
123 {
124         int regval;
125
126         if (phydev->dev_flags & MICREL_PHY_50MHZ_CLK) {
127                 regval = phy_read(phydev, MII_KSZPHY_CTRL);
128                 regval |= KSZ8051_RMII_50MHZ_CLK;
129                 phy_write(phydev, MII_KSZPHY_CTRL, regval);
130         }
131
132         return 0;
133 }
134
135 #define KSZ8873MLL_GLOBAL_CONTROL_4     0x06
136 #define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX      (1 << 6)
137 #define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED       (1 << 4)
138 int ksz8873mll_read_status(struct phy_device *phydev)
139 {
140         int regval;
141
142         /* dummy read */
143         regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);
144
145         regval = phy_read(phydev, KSZ8873MLL_GLOBAL_CONTROL_4);
146
147         if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX)
148                 phydev->duplex = DUPLEX_HALF;
149         else
150                 phydev->duplex = DUPLEX_FULL;
151
152         if (regval & KSZ8873MLL_GLOBAL_CONTROL_4_SPEED)
153                 phydev->speed = SPEED_10;
154         else
155                 phydev->speed = SPEED_100;
156
157         phydev->link = 1;
158         phydev->pause = phydev->asym_pause = 0;
159
160         return 0;
161 }
162
163 static int ksz8873mll_config_aneg(struct phy_device *phydev)
164 {
165         return 0;
166 }
167
168 static struct phy_driver ksphy_driver[] = {
169 {
170         .phy_id         = PHY_ID_KS8737,
171         .phy_id_mask    = 0x00fffff0,
172         .name           = "Micrel KS8737",
173         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
174         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
175         .config_init    = kszphy_config_init,
176         .config_aneg    = genphy_config_aneg,
177         .read_status    = genphy_read_status,
178         .ack_interrupt  = kszphy_ack_interrupt,
179         .config_intr    = ks8737_config_intr,
180         .driver         = { .owner = THIS_MODULE,},
181 }, {
182         .phy_id         = PHY_ID_KSZ8021,
183         .phy_id_mask    = 0x00ffffff,
184         .name           = "Micrel KSZ8021 or KSZ8031",
185         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause |
186                            SUPPORTED_Asym_Pause),
187         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
188         .config_init    = ksz8021_config_init,
189         .config_aneg    = genphy_config_aneg,
190         .read_status    = genphy_read_status,
191         .ack_interrupt  = kszphy_ack_interrupt,
192         .config_intr    = kszphy_config_intr,
193         .driver         = { .owner = THIS_MODULE,},
194 }, {
195         .phy_id         = PHY_ID_KSZ8041,
196         .phy_id_mask    = 0x00fffff0,
197         .name           = "Micrel KSZ8041",
198         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
199                                 | SUPPORTED_Asym_Pause),
200         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
201         .config_init    = kszphy_config_init,
202         .config_aneg    = genphy_config_aneg,
203         .read_status    = genphy_read_status,
204         .ack_interrupt  = kszphy_ack_interrupt,
205         .config_intr    = kszphy_config_intr,
206         .driver         = { .owner = THIS_MODULE,},
207 }, {
208         .phy_id         = PHY_ID_KSZ8051,
209         .phy_id_mask    = 0x00fffff0,
210         .name           = "Micrel KSZ8051",
211         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
212                                 | SUPPORTED_Asym_Pause),
213         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
214         .config_init    = ks8051_config_init,
215         .config_aneg    = genphy_config_aneg,
216         .read_status    = genphy_read_status,
217         .ack_interrupt  = kszphy_ack_interrupt,
218         .config_intr    = kszphy_config_intr,
219         .driver         = { .owner = THIS_MODULE,},
220 }, {
221         .phy_id         = PHY_ID_KSZ8001,
222         .name           = "Micrel KSZ8001 or KS8721",
223         .phy_id_mask    = 0x00ffffff,
224         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
225         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
226         .config_init    = kszphy_config_init,
227         .config_aneg    = genphy_config_aneg,
228         .read_status    = genphy_read_status,
229         .ack_interrupt  = kszphy_ack_interrupt,
230         .config_intr    = kszphy_config_intr,
231         .driver         = { .owner = THIS_MODULE,},
232 }, {
233         .phy_id         = PHY_ID_KSZ8081,
234         .name           = "Micrel KSZ8081 or KSZ8091",
235         .phy_id_mask    = 0x00fffff0,
236         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
237         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
238         .config_init    = kszphy_config_init,
239         .config_aneg    = genphy_config_aneg,
240         .read_status    = genphy_read_status,
241         .ack_interrupt  = kszphy_ack_interrupt,
242         .config_intr    = kszphy_config_intr,
243         .driver         = { .owner = THIS_MODULE,},
244 }, {
245         .phy_id         = PHY_ID_KSZ8061,
246         .name           = "Micrel KSZ8061",
247         .phy_id_mask    = 0x00fffff0,
248         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
249         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
250         .config_init    = kszphy_config_init,
251         .config_aneg    = genphy_config_aneg,
252         .read_status    = genphy_read_status,
253         .ack_interrupt  = kszphy_ack_interrupt,
254         .config_intr    = kszphy_config_intr,
255         .driver         = { .owner = THIS_MODULE,},
256 }, {
257         .phy_id         = PHY_ID_KSZ9021,
258         .phy_id_mask    = 0x000ffffe,
259         .name           = "Micrel KSZ9021 Gigabit PHY",
260         .features       = (PHY_GBIT_FEATURES | SUPPORTED_Pause),
261         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
262         .config_init    = kszphy_config_init,
263         .config_aneg    = genphy_config_aneg,
264         .read_status    = genphy_read_status,
265         .ack_interrupt  = kszphy_ack_interrupt,
266         .config_intr    = ksz9021_config_intr,
267         .driver         = { .owner = THIS_MODULE, },
268 }, {
269         .phy_id         = PHY_ID_KSZ9031,
270         .phy_id_mask    = 0x00fffff0,
271         .name           = "Micrel KSZ9031 Gigabit PHY",
272         .features       = (PHY_GBIT_FEATURES | SUPPORTED_Pause
273                                 | SUPPORTED_Asym_Pause),
274         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
275         .config_init    = kszphy_config_init,
276         .config_aneg    = genphy_config_aneg,
277         .read_status    = genphy_read_status,
278         .ack_interrupt  = kszphy_ack_interrupt,
279         .config_intr    = ksz9021_config_intr,
280         .driver         = { .owner = THIS_MODULE, },
281 }, {
282         .phy_id         = PHY_ID_KSZ8873MLL,
283         .phy_id_mask    = 0x00fffff0,
284         .name           = "Micrel KSZ8873MLL Switch",
285         .features       = (SUPPORTED_Pause | SUPPORTED_Asym_Pause),
286         .flags          = PHY_HAS_MAGICANEG,
287         .config_init    = kszphy_config_init,
288         .config_aneg    = ksz8873mll_config_aneg,
289         .read_status    = ksz8873mll_read_status,
290         .driver         = { .owner = THIS_MODULE, },
291 }, {
292         .phy_id         = PHY_ID_KSZ886X,
293         .phy_id_mask    = 0x00fffff0,
294         .name           = "Micrel KSZ886X Switch",
295         .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
296         .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
297         .config_init    = kszphy_config_init,
298         .config_aneg    = genphy_config_aneg,
299         .read_status    = genphy_read_status,
300         .driver         = { .owner = THIS_MODULE, },
301 } };
302
303 static int __init ksphy_init(void)
304 {
305         return phy_drivers_register(ksphy_driver,
306                 ARRAY_SIZE(ksphy_driver));
307 }
308
309 static void __exit ksphy_exit(void)
310 {
311         phy_drivers_unregister(ksphy_driver,
312                 ARRAY_SIZE(ksphy_driver));
313 }
314
315 module_init(ksphy_init);
316 module_exit(ksphy_exit);
317
318 MODULE_DESCRIPTION("Micrel PHY driver");
319 MODULE_AUTHOR("David J. Choi");
320 MODULE_LICENSE("GPL");
321
322 static struct mdio_device_id __maybe_unused micrel_tbl[] = {
323         { PHY_ID_KSZ9021, 0x000ffffe },
324         { PHY_ID_KSZ9031, 0x00fffff0 },
325         { PHY_ID_KSZ8001, 0x00ffffff },
326         { PHY_ID_KS8737, 0x00fffff0 },
327         { PHY_ID_KSZ8021, 0x00ffffff },
328         { PHY_ID_KSZ8041, 0x00fffff0 },
329         { PHY_ID_KSZ8051, 0x00fffff0 },
330         { PHY_ID_KSZ8061, 0x00fffff0 },
331         { PHY_ID_KSZ8081, 0x00fffff0 },
332         { PHY_ID_KSZ8873MLL, 0x00fffff0 },
333         { PHY_ID_KSZ886X, 0x00fffff0 },
334         { }
335 };
336
337 MODULE_DEVICE_TABLE(mdio, micrel_tbl);