Merge branch 'kbuild' of git://git.kernel.org/pub/scm/linux/kernel/git/mmarek/kbuild
[cascardo/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_DEBUG_ADDR                       0x1D
31 #define AT803X_DEBUG_DATA                       0x1E
32 #define AT803X_DEBUG_SYSTEM_MODE_CTRL           0x05
33 #define AT803X_DEBUG_RGMII_TX_CLK_DLY           BIT(8)
34
35 MODULE_DESCRIPTION("Atheros 803x PHY driver");
36 MODULE_AUTHOR("Matus Ujhelyi");
37 MODULE_LICENSE("GPL");
38
39 static int at803x_set_wol(struct phy_device *phydev,
40                           struct ethtool_wolinfo *wol)
41 {
42         struct net_device *ndev = phydev->attached_dev;
43         const u8 *mac;
44         int ret;
45         u32 value;
46         unsigned int i, offsets[] = {
47                 AT803X_LOC_MAC_ADDR_32_47_OFFSET,
48                 AT803X_LOC_MAC_ADDR_16_31_OFFSET,
49                 AT803X_LOC_MAC_ADDR_0_15_OFFSET,
50         };
51
52         if (!ndev)
53                 return -ENODEV;
54
55         if (wol->wolopts & WAKE_MAGIC) {
56                 mac = (const u8 *) ndev->dev_addr;
57
58                 if (!is_valid_ether_addr(mac))
59                         return -EFAULT;
60
61                 for (i = 0; i < 3; i++) {
62                         phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
63                                   AT803X_DEVICE_ADDR);
64                         phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
65                                   offsets[i]);
66                         phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
67                                   AT803X_FUNC_DATA);
68                         phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
69                                   mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
70                 }
71
72                 value = phy_read(phydev, AT803X_INTR_ENABLE);
73                 value |= AT803X_WOL_ENABLE;
74                 ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
75                 if (ret)
76                         return ret;
77                 value = phy_read(phydev, AT803X_INTR_STATUS);
78         } else {
79                 value = phy_read(phydev, AT803X_INTR_ENABLE);
80                 value &= (~AT803X_WOL_ENABLE);
81                 ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
82                 if (ret)
83                         return ret;
84                 value = phy_read(phydev, AT803X_INTR_STATUS);
85         }
86
87         return ret;
88 }
89
90 static void at803x_get_wol(struct phy_device *phydev,
91                            struct ethtool_wolinfo *wol)
92 {
93         u32 value;
94
95         wol->supported = WAKE_MAGIC;
96         wol->wolopts = 0;
97
98         value = phy_read(phydev, AT803X_INTR_ENABLE);
99         if (value & AT803X_WOL_ENABLE)
100                 wol->wolopts |= WAKE_MAGIC;
101 }
102
103 static int at803x_config_init(struct phy_device *phydev)
104 {
105         int val;
106         int ret;
107         u32 features;
108
109         features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI |
110                    SUPPORTED_FIBRE | SUPPORTED_BNC;
111
112         val = phy_read(phydev, MII_BMSR);
113         if (val < 0)
114                 return val;
115
116         if (val & BMSR_ANEGCAPABLE)
117                 features |= SUPPORTED_Autoneg;
118         if (val & BMSR_100FULL)
119                 features |= SUPPORTED_100baseT_Full;
120         if (val & BMSR_100HALF)
121                 features |= SUPPORTED_100baseT_Half;
122         if (val & BMSR_10FULL)
123                 features |= SUPPORTED_10baseT_Full;
124         if (val & BMSR_10HALF)
125                 features |= SUPPORTED_10baseT_Half;
126
127         if (val & BMSR_ESTATEN) {
128                 val = phy_read(phydev, MII_ESTATUS);
129                 if (val < 0)
130                         return val;
131
132                 if (val & ESTATUS_1000_TFULL)
133                         features |= SUPPORTED_1000baseT_Full;
134                 if (val & ESTATUS_1000_THALF)
135                         features |= SUPPORTED_1000baseT_Half;
136         }
137
138         phydev->supported = features;
139         phydev->advertising = features;
140
141         if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
142                 ret = phy_write(phydev, AT803X_DEBUG_ADDR,
143                                 AT803X_DEBUG_SYSTEM_MODE_CTRL);
144                 if (ret)
145                         return ret;
146                 ret = phy_write(phydev, AT803X_DEBUG_DATA,
147                                 AT803X_DEBUG_RGMII_TX_CLK_DLY);
148                 if (ret)
149                         return ret;
150         }
151
152         return 0;
153 }
154
155 static struct phy_driver at803x_driver[] = {
156 {
157         /* ATHEROS 8035 */
158         .phy_id         = 0x004dd072,
159         .name           = "Atheros 8035 ethernet",
160         .phy_id_mask    = 0xffffffef,
161         .config_init    = at803x_config_init,
162         .set_wol        = at803x_set_wol,
163         .get_wol        = at803x_get_wol,
164         .features       = PHY_GBIT_FEATURES,
165         .flags          = PHY_HAS_INTERRUPT,
166         .config_aneg    = &genphy_config_aneg,
167         .read_status    = &genphy_read_status,
168         .driver         = {
169                 .owner = THIS_MODULE,
170         },
171 }, {
172         /* ATHEROS 8030 */
173         .phy_id         = 0x004dd076,
174         .name           = "Atheros 8030 ethernet",
175         .phy_id_mask    = 0xffffffef,
176         .config_init    = at803x_config_init,
177         .set_wol        = at803x_set_wol,
178         .get_wol        = at803x_get_wol,
179         .features       = PHY_GBIT_FEATURES,
180         .flags          = PHY_HAS_INTERRUPT,
181         .config_aneg    = &genphy_config_aneg,
182         .read_status    = &genphy_read_status,
183         .driver         = {
184                 .owner = THIS_MODULE,
185         },
186 }, {
187         /* ATHEROS 8031 */
188         .phy_id         = 0x004dd074,
189         .name           = "Atheros 8031 ethernet",
190         .phy_id_mask    = 0xffffffef,
191         .config_init    = at803x_config_init,
192         .set_wol        = at803x_set_wol,
193         .get_wol        = at803x_get_wol,
194         .features       = PHY_GBIT_FEATURES,
195         .flags          = PHY_HAS_INTERRUPT,
196         .config_aneg    = &genphy_config_aneg,
197         .read_status    = &genphy_read_status,
198         .driver         = {
199                 .owner = THIS_MODULE,
200         },
201 } };
202
203 static int __init atheros_init(void)
204 {
205         return phy_drivers_register(at803x_driver,
206                                     ARRAY_SIZE(at803x_driver));
207 }
208
209 static void __exit atheros_exit(void)
210 {
211         return phy_drivers_unregister(at803x_driver,
212                                       ARRAY_SIZE(at803x_driver));
213 }
214
215 module_init(atheros_init);
216 module_exit(atheros_exit);
217
218 static struct mdio_device_id __maybe_unused atheros_tbl[] = {
219         { 0x004dd076, 0xffffffef },
220         { 0x004dd072, 0xffffffef },
221         { }
222 };
223
224 MODULE_DEVICE_TABLE(mdio, atheros_tbl);