]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/net/phy/at803x.c
ARM: imx6: add defconfig for Ka-Ro TX6 modules
[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 ret;
148
149         ret = genphy_config_init(phydev);
150         if (ret < 0)
151                 return ret;
152
153         if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
154                 ret = phy_write(phydev, AT803X_DEBUG_ADDR,
155                                 AT803X_DEBUG_SYSTEM_MODE_CTRL);
156                 if (ret)
157                         return ret;
158                 ret = phy_write(phydev, AT803X_DEBUG_DATA,
159                                 AT803X_DEBUG_RGMII_TX_CLK_DLY);
160                 if (ret)
161                         return ret;
162         }
163
164         return 0;
165 }
166
167 static int at803x_ack_interrupt(struct phy_device *phydev)
168 {
169         int err;
170
171         err = phy_read(phydev, AT803X_INSR);
172
173         return (err < 0) ? err : 0;
174 }
175
176 static int at803x_config_intr(struct phy_device *phydev)
177 {
178         int err;
179         int value;
180
181         value = phy_read(phydev, AT803X_INER);
182
183         if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
184                 err = phy_write(phydev, AT803X_INER,
185                                 value | AT803X_INER_INIT);
186         else
187                 err = phy_write(phydev, AT803X_INER, 0);
188
189         return err;
190 }
191
192 static struct phy_driver at803x_driver[] = {
193 {
194         /* ATHEROS 8035 */
195         .phy_id         = 0x004dd072,
196         .name           = "Atheros 8035 ethernet",
197         .phy_id_mask    = 0xffffffef,
198         .config_init    = at803x_config_init,
199         .set_wol        = at803x_set_wol,
200         .get_wol        = at803x_get_wol,
201         .suspend        = at803x_suspend,
202         .resume         = at803x_resume,
203         .features       = PHY_GBIT_FEATURES,
204         .flags          = PHY_HAS_INTERRUPT,
205         .config_aneg    = genphy_config_aneg,
206         .read_status    = genphy_read_status,
207         .driver         = {
208                 .owner = THIS_MODULE,
209         },
210 }, {
211         /* ATHEROS 8030 */
212         .phy_id         = 0x004dd076,
213         .name           = "Atheros 8030 ethernet",
214         .phy_id_mask    = 0xffffffef,
215         .config_init    = at803x_config_init,
216         .set_wol        = at803x_set_wol,
217         .get_wol        = at803x_get_wol,
218         .suspend        = at803x_suspend,
219         .resume         = at803x_resume,
220         .features       = PHY_GBIT_FEATURES,
221         .flags          = PHY_HAS_INTERRUPT,
222         .config_aneg    = genphy_config_aneg,
223         .read_status    = genphy_read_status,
224         .driver         = {
225                 .owner = THIS_MODULE,
226         },
227 }, {
228         /* ATHEROS 8031 */
229         .phy_id         = 0x004dd074,
230         .name           = "Atheros 8031 ethernet",
231         .phy_id_mask    = 0xffffffef,
232         .config_init    = at803x_config_init,
233         .set_wol        = at803x_set_wol,
234         .get_wol        = at803x_get_wol,
235         .suspend        = at803x_suspend,
236         .resume         = at803x_resume,
237         .features       = PHY_GBIT_FEATURES,
238         .flags          = PHY_HAS_INTERRUPT,
239         .config_aneg    = genphy_config_aneg,
240         .read_status    = genphy_read_status,
241         .ack_interrupt  = &at803x_ack_interrupt,
242         .config_intr    = &at803x_config_intr,
243         .driver         = {
244                 .owner = THIS_MODULE,
245         },
246 } };
247
248 static int __init atheros_init(void)
249 {
250         return phy_drivers_register(at803x_driver,
251                                     ARRAY_SIZE(at803x_driver));
252 }
253
254 static void __exit atheros_exit(void)
255 {
256         phy_drivers_unregister(at803x_driver, ARRAY_SIZE(at803x_driver));
257 }
258
259 module_init(atheros_init);
260 module_exit(atheros_exit);
261
262 static struct mdio_device_id __maybe_unused atheros_tbl[] = {
263         { 0x004dd076, 0xffffffef },
264         { 0x004dd074, 0xffffffef },
265         { 0x004dd072, 0xffffffef },
266         { }
267 };
268
269 MODULE_DEVICE_TABLE(mdio, atheros_tbl);