]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/net/phy/at803x.c
Merge tag 'at91-fixes2' of git://github.com/at91linux/linux-at91 into fixes
[karo-tx-linux.git] / drivers / net / phy / at803x.c
1 /*
2  * drivers/net/phy/at803x.c
3  *
4  * Driver for Atheros 803x PHY
5  *
6  * Author: Matus Ujhelyi <ujhelyi.m@gmail.com>
7  *
8  * This program is free software; you can redistribute  it and/or modify it
9  * under  the terms of  the GNU General  Public License as published by the
10  * Free Software Foundation;  either version 2 of the  License, or (at your
11  * option) any later version.
12  */
13
14 #include <linux/phy.h>
15 #include <linux/module.h>
16 #include <linux/string.h>
17 #include <linux/netdevice.h>
18 #include <linux/etherdevice.h>
19
20 #define AT803X_INTR_ENABLE                      0x12
21 #define AT803X_INTR_STATUS                      0x13
22 #define AT803X_WOL_ENABLE                       0x01
23 #define AT803X_DEVICE_ADDR                      0x03
24 #define AT803X_LOC_MAC_ADDR_0_15_OFFSET         0x804C
25 #define AT803X_LOC_MAC_ADDR_16_31_OFFSET        0x804B
26 #define AT803X_LOC_MAC_ADDR_32_47_OFFSET        0x804A
27 #define AT803X_MMD_ACCESS_CONTROL               0x0D
28 #define AT803X_MMD_ACCESS_CONTROL_DATA          0x0E
29 #define AT803X_FUNC_DATA                        0x4003
30 #define AT803X_INER                             0x0012
31 #define AT803X_INER_INIT                        0xec00
32 #define AT803X_INSR                             0x0013
33 #define AT803X_DEBUG_ADDR                       0x1D
34 #define AT803X_DEBUG_DATA                       0x1E
35 #define AT803X_DEBUG_SYSTEM_MODE_CTRL           0x05
36 #define AT803X_DEBUG_RGMII_TX_CLK_DLY           BIT(8)
37
38 MODULE_DESCRIPTION("Atheros 803x PHY driver");
39 MODULE_AUTHOR("Matus Ujhelyi");
40 MODULE_LICENSE("GPL");
41
42 static int at803x_set_wol(struct phy_device *phydev,
43                           struct ethtool_wolinfo *wol)
44 {
45         struct net_device *ndev = phydev->attached_dev;
46         const u8 *mac;
47         int ret;
48         u32 value;
49         unsigned int i, offsets[] = {
50                 AT803X_LOC_MAC_ADDR_32_47_OFFSET,
51                 AT803X_LOC_MAC_ADDR_16_31_OFFSET,
52                 AT803X_LOC_MAC_ADDR_0_15_OFFSET,
53         };
54
55         if (!ndev)
56                 return -ENODEV;
57
58         if (wol->wolopts & WAKE_MAGIC) {
59                 mac = (const u8 *) ndev->dev_addr;
60
61                 if (!is_valid_ether_addr(mac))
62                         return -EFAULT;
63
64                 for (i = 0; i < 3; i++) {
65                         phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
66                                   AT803X_DEVICE_ADDR);
67                         phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
68                                   offsets[i]);
69                         phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
70                                   AT803X_FUNC_DATA);
71                         phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
72                                   mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
73                 }
74
75                 value = phy_read(phydev, AT803X_INTR_ENABLE);
76                 value |= AT803X_WOL_ENABLE;
77                 ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
78                 if (ret)
79                         return ret;
80                 value = phy_read(phydev, AT803X_INTR_STATUS);
81         } else {
82                 value = phy_read(phydev, AT803X_INTR_ENABLE);
83                 value &= (~AT803X_WOL_ENABLE);
84                 ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
85                 if (ret)
86                         return ret;
87                 value = phy_read(phydev, AT803X_INTR_STATUS);
88         }
89
90         return ret;
91 }
92
93 static void at803x_get_wol(struct phy_device *phydev,
94                            struct ethtool_wolinfo *wol)
95 {
96         u32 value;
97
98         wol->supported = WAKE_MAGIC;
99         wol->wolopts = 0;
100
101         value = phy_read(phydev, AT803X_INTR_ENABLE);
102         if (value & AT803X_WOL_ENABLE)
103                 wol->wolopts |= WAKE_MAGIC;
104 }
105
106 static int at803x_suspend(struct phy_device *phydev)
107 {
108         int value;
109         int wol_enabled;
110
111         mutex_lock(&phydev->lock);
112
113         value = phy_read(phydev, AT803X_INTR_ENABLE);
114         wol_enabled = value & AT803X_WOL_ENABLE;
115
116         value = phy_read(phydev, MII_BMCR);
117
118         if (wol_enabled)
119                 value |= BMCR_ISOLATE;
120         else
121                 value |= BMCR_PDOWN;
122
123         phy_write(phydev, MII_BMCR, value);
124
125         mutex_unlock(&phydev->lock);
126
127         return 0;
128 }
129
130 static int at803x_resume(struct phy_device *phydev)
131 {
132         int value;
133
134         mutex_lock(&phydev->lock);
135
136         value = phy_read(phydev, MII_BMCR);
137         value &= ~(BMCR_PDOWN | BMCR_ISOLATE);
138         phy_write(phydev, MII_BMCR, value);
139
140         mutex_unlock(&phydev->lock);
141
142         return 0;
143 }
144
145 static int at803x_config_init(struct phy_device *phydev)
146 {
147         int val;
148         int ret;
149         u32 features;
150
151         features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI |
152                    SUPPORTED_FIBRE | SUPPORTED_BNC;
153
154         val = phy_read(phydev, MII_BMSR);
155         if (val < 0)
156                 return val;
157
158         if (val & BMSR_ANEGCAPABLE)
159                 features |= SUPPORTED_Autoneg;
160         if (val & BMSR_100FULL)
161                 features |= SUPPORTED_100baseT_Full;
162         if (val & BMSR_100HALF)
163                 features |= SUPPORTED_100baseT_Half;
164         if (val & BMSR_10FULL)
165                 features |= SUPPORTED_10baseT_Full;
166         if (val & BMSR_10HALF)
167                 features |= SUPPORTED_10baseT_Half;
168
169         if (val & BMSR_ESTATEN) {
170                 val = phy_read(phydev, MII_ESTATUS);
171                 if (val < 0)
172                         return val;
173
174                 if (val & ESTATUS_1000_TFULL)
175                         features |= SUPPORTED_1000baseT_Full;
176                 if (val & ESTATUS_1000_THALF)
177                         features |= SUPPORTED_1000baseT_Half;
178         }
179
180         phydev->supported = features;
181         phydev->advertising = features;
182
183         if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
184                 ret = phy_write(phydev, AT803X_DEBUG_ADDR,
185                                 AT803X_DEBUG_SYSTEM_MODE_CTRL);
186                 if (ret)
187                         return ret;
188                 ret = phy_write(phydev, AT803X_DEBUG_DATA,
189                                 AT803X_DEBUG_RGMII_TX_CLK_DLY);
190                 if (ret)
191                         return ret;
192         }
193
194         return 0;
195 }
196
197 static int at803x_ack_interrupt(struct phy_device *phydev)
198 {
199         int err;
200
201         err = phy_read(phydev, AT803X_INSR);
202
203         return (err < 0) ? err : 0;
204 }
205
206 static int at803x_config_intr(struct phy_device *phydev)
207 {
208         int err;
209         int value;
210
211         value = phy_read(phydev, AT803X_INER);
212
213         if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
214                 err = phy_write(phydev, AT803X_INER,
215                                 value | AT803X_INER_INIT);
216         else
217                 err = phy_write(phydev, AT803X_INER, 0);
218
219         return err;
220 }
221
222 static struct phy_driver at803x_driver[] = {
223 {
224         /* ATHEROS 8035 */
225         .phy_id         = 0x004dd072,
226         .name           = "Atheros 8035 ethernet",
227         .phy_id_mask    = 0xffffffef,
228         .config_init    = at803x_config_init,
229         .set_wol        = at803x_set_wol,
230         .get_wol        = at803x_get_wol,
231         .suspend        = at803x_suspend,
232         .resume         = at803x_resume,
233         .features       = PHY_GBIT_FEATURES,
234         .flags          = PHY_HAS_INTERRUPT,
235         .config_aneg    = genphy_config_aneg,
236         .read_status    = genphy_read_status,
237         .driver         = {
238                 .owner = THIS_MODULE,
239         },
240 }, {
241         /* ATHEROS 8030 */
242         .phy_id         = 0x004dd076,
243         .name           = "Atheros 8030 ethernet",
244         .phy_id_mask    = 0xffffffef,
245         .config_init    = at803x_config_init,
246         .set_wol        = at803x_set_wol,
247         .get_wol        = at803x_get_wol,
248         .suspend        = at803x_suspend,
249         .resume         = at803x_resume,
250         .features       = PHY_GBIT_FEATURES,
251         .flags          = PHY_HAS_INTERRUPT,
252         .config_aneg    = genphy_config_aneg,
253         .read_status    = genphy_read_status,
254         .driver         = {
255                 .owner = THIS_MODULE,
256         },
257 }, {
258         /* ATHEROS 8031 */
259         .phy_id         = 0x004dd074,
260         .name           = "Atheros 8031 ethernet",
261         .phy_id_mask    = 0xffffffef,
262         .config_init    = at803x_config_init,
263         .set_wol        = at803x_set_wol,
264         .get_wol        = at803x_get_wol,
265         .suspend        = at803x_suspend,
266         .resume         = at803x_resume,
267         .features       = PHY_GBIT_FEATURES,
268         .flags          = PHY_HAS_INTERRUPT,
269         .config_aneg    = genphy_config_aneg,
270         .read_status    = genphy_read_status,
271         .ack_interrupt  = &at803x_ack_interrupt,
272         .config_intr    = &at803x_config_intr,
273         .driver         = {
274                 .owner = THIS_MODULE,
275         },
276 } };
277
278 static int __init atheros_init(void)
279 {
280         return phy_drivers_register(at803x_driver,
281                                     ARRAY_SIZE(at803x_driver));
282 }
283
284 static void __exit atheros_exit(void)
285 {
286         return phy_drivers_unregister(at803x_driver,
287                                       ARRAY_SIZE(at803x_driver));
288 }
289
290 module_init(atheros_init);
291 module_exit(atheros_exit);
292
293 static struct mdio_device_id __maybe_unused atheros_tbl[] = {
294         { 0x004dd076, 0xffffffef },
295         { 0x004dd074, 0xffffffef },
296         { 0x004dd072, 0xffffffef },
297         { }
298 };
299
300 MODULE_DEVICE_TABLE(mdio, atheros_tbl);