mwifiex: channel switch handling for station
[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
31 MODULE_DESCRIPTION("Atheros 803x PHY driver");
32 MODULE_AUTHOR("Matus Ujhelyi");
33 MODULE_LICENSE("GPL");
34
35 static void at803x_set_wol_mac_addr(struct phy_device *phydev)
36 {
37         struct net_device *ndev = phydev->attached_dev;
38         const u8 *mac;
39         unsigned int i, offsets[] = {
40                 AT803X_LOC_MAC_ADDR_32_47_OFFSET,
41                 AT803X_LOC_MAC_ADDR_16_31_OFFSET,
42                 AT803X_LOC_MAC_ADDR_0_15_OFFSET,
43         };
44
45         if (!ndev)
46                 return;
47
48         mac = (const u8 *) ndev->dev_addr;
49
50         if (!is_valid_ether_addr(mac))
51                 return;
52
53         for (i = 0; i < 3; i++) {
54                 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
55                                   AT803X_DEVICE_ADDR);
56                 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
57                                   offsets[i]);
58                 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
59                                   AT803X_FUNC_DATA);
60                 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
61                                   mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
62         }
63 }
64
65 static int at803x_config_init(struct phy_device *phydev)
66 {
67         int val;
68         u32 features;
69         int status;
70
71         features = SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI |
72                    SUPPORTED_FIBRE | SUPPORTED_BNC;
73
74         val = phy_read(phydev, MII_BMSR);
75         if (val < 0)
76                 return val;
77
78         if (val & BMSR_ANEGCAPABLE)
79                 features |= SUPPORTED_Autoneg;
80         if (val & BMSR_100FULL)
81                 features |= SUPPORTED_100baseT_Full;
82         if (val & BMSR_100HALF)
83                 features |= SUPPORTED_100baseT_Half;
84         if (val & BMSR_10FULL)
85                 features |= SUPPORTED_10baseT_Full;
86         if (val & BMSR_10HALF)
87                 features |= SUPPORTED_10baseT_Half;
88
89         if (val & BMSR_ESTATEN) {
90                 val = phy_read(phydev, MII_ESTATUS);
91                 if (val < 0)
92                         return val;
93
94                 if (val & ESTATUS_1000_TFULL)
95                         features |= SUPPORTED_1000baseT_Full;
96                 if (val & ESTATUS_1000_THALF)
97                         features |= SUPPORTED_1000baseT_Half;
98         }
99
100         phydev->supported = features;
101         phydev->advertising = features;
102
103         /* enable WOL */
104         at803x_set_wol_mac_addr(phydev);
105         status = phy_write(phydev, AT803X_INTR_ENABLE, AT803X_WOL_ENABLE);
106         status = phy_read(phydev, AT803X_INTR_STATUS);
107
108         return 0;
109 }
110
111 /* ATHEROS 8035 */
112 static struct phy_driver at8035_driver = {
113         .phy_id         = 0x004dd072,
114         .name           = "Atheros 8035 ethernet",
115         .phy_id_mask    = 0xffffffef,
116         .config_init    = at803x_config_init,
117         .features       = PHY_GBIT_FEATURES,
118         .flags          = PHY_HAS_INTERRUPT,
119         .config_aneg    = &genphy_config_aneg,
120         .read_status    = &genphy_read_status,
121         .driver         = {
122                 .owner = THIS_MODULE,
123         },
124 };
125
126 /* ATHEROS 8030 */
127 static struct phy_driver at8030_driver = {
128         .phy_id         = 0x004dd076,
129         .name           = "Atheros 8030 ethernet",
130         .phy_id_mask    = 0xffffffef,
131         .config_init    = at803x_config_init,
132         .features       = PHY_GBIT_FEATURES,
133         .flags          = PHY_HAS_INTERRUPT,
134         .config_aneg    = &genphy_config_aneg,
135         .read_status    = &genphy_read_status,
136         .driver         = {
137                 .owner = THIS_MODULE,
138         },
139 };
140
141 static int __init atheros_init(void)
142 {
143         int ret;
144
145         ret = phy_driver_register(&at8035_driver);
146         if (ret)
147                 goto err1;
148
149         ret = phy_driver_register(&at8030_driver);
150         if (ret)
151                 goto err2;
152
153         return 0;
154
155 err2:
156         phy_driver_unregister(&at8035_driver);
157 err1:
158         return ret;
159 }
160
161 static void __exit atheros_exit(void)
162 {
163         phy_driver_unregister(&at8035_driver);
164         phy_driver_unregister(&at8030_driver);
165 }
166
167 module_init(atheros_init);
168 module_exit(atheros_exit);
169
170 static struct mdio_device_id __maybe_unused atheros_tbl[] = {
171         { 0x004dd076, 0xffffffef },
172         { 0x004dd072, 0xffffffef },
173         { }
174 };
175
176 MODULE_DEVICE_TABLE(mdio, atheros_tbl);