source: trunk/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_phy.c @ 13369

Last change on this file since 13369 was 13369, checked in by juhosg, 8 years ago

[ar71xx] ag71xx: introduce SoC specific fuctions for DDR flush and PLL setup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 5.7 KB
Line 
1/*
2 *  Atheros AR71xx built-in ethernet mac driver
3 *
4 *  Copyright (C) 2008 Gabor Juhos <juhosg@openwrt.org>
5 *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
6 *
7 *  Based on Atheros' AG7100 driver
8 *
9 *  This program is free software; you can redistribute it and/or modify it
10 *  under the terms of the GNU General Public License version 2 as published
11 *  by the Free Software Foundation.
12 */
13
14#include "ag71xx.h"
15
16static unsigned char *ag71xx_speed_str(struct ag71xx *ag)
17{
18        switch (ag->speed) {
19        case SPEED_1000:
20                return "1000";
21        case SPEED_100:
22                return "100";
23        case SPEED_10:
24                return "10";
25        }
26
27        return "?";
28}
29
30#if 1
31#define PLL_VAL_1000    0x00110000
32#define PLL_VAL_100     0x00001099
33#define PLL_VAL_10      0x00991099
34#else
35#define PLL_VAL_1000    0x01111000
36#define PLL_VAL_100     0x09991000
37#define PLL_VAL_10      0x09991999
38#endif
39
40static void ag71xx_phy_link_update(struct ag71xx *ag)
41{
42        struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
43        u32 cfg2;
44        u32 ifctl;
45        u32 pll;
46        u32 fifo5;
47        u32 mii_speed;
48
49        if (!ag->link) {
50                netif_carrier_off(ag->dev);
51                if (netif_msg_link(ag))
52                        printk(KERN_INFO "%s: link down\n", ag->dev->name);
53                return;
54        }
55
56        cfg2 = ag71xx_rr(ag, AG71XX_REG_MAC_CFG2);
57        cfg2 &= ~(MAC_CFG2_IF_1000 | MAC_CFG2_IF_10_100 | MAC_CFG2_FDX);
58        cfg2 |= (ag->duplex) ? MAC_CFG2_FDX : 0;
59
60        ifctl = ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL);
61        ifctl &= ~(MAC_IFCTL_SPEED);
62
63        fifo5 = ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5);
64        fifo5 &= ~FIFO_CFG5_BM;
65
66        switch (ag->speed) {
67        case SPEED_1000:
68                mii_speed =  MII_CTRL_SPEED_1000;
69                cfg2 |= MAC_CFG2_IF_1000;
70                pll = PLL_VAL_1000;
71                fifo5 |= FIFO_CFG5_BM;
72                break;
73        case SPEED_100:
74                mii_speed = MII_CTRL_SPEED_100;
75                cfg2 |= MAC_CFG2_IF_10_100;
76                ifctl |= MAC_IFCTL_SPEED;
77                pll = PLL_VAL_100;
78                break;
79        case SPEED_10:
80                mii_speed = MII_CTRL_SPEED_10;
81                cfg2 |= MAC_CFG2_IF_10_100;
82                pll = PLL_VAL_10;
83                break;
84        default:
85                BUG();
86                return;
87        }
88
89        ag71xx_wr(ag, AG71XX_REG_FIFO_CFG3, 0x008001ff);
90        pdata->set_pll(pll);
91        ag71xx_mii_ctrl_set_speed(ag, mii_speed);
92
93        ag71xx_wr(ag, AG71XX_REG_MAC_CFG2, cfg2);
94        ag71xx_wr(ag, AG71XX_REG_FIFO_CFG5, fifo5);
95        ag71xx_wr(ag, AG71XX_REG_MAC_IFCTL, ifctl);
96
97        netif_carrier_on(ag->dev);
98        if (netif_msg_link(ag))
99                printk(KERN_INFO "%s: link up (%sMbps/%s duplex)\n",
100                        ag->dev->name,
101                        ag71xx_speed_str(ag),
102                        (DUPLEX_FULL == ag->duplex) ? "Full" : "Half");
103
104        DBG("%s: fifo_cfg0=%#x, fifo_cfg1=%#x, fifo_cfg2=%#x\n",
105                ag->dev->name,
106                ag71xx_rr(ag, AG71XX_REG_FIFO_CFG0),
107                ag71xx_rr(ag, AG71XX_REG_FIFO_CFG1),
108                ag71xx_rr(ag, AG71XX_REG_FIFO_CFG2));
109
110        DBG("%s: fifo_cfg3=%#x, fifo_cfg4=%#x, fifo_cfg5=%#x\n",
111                ag->dev->name,
112                ag71xx_rr(ag, AG71XX_REG_FIFO_CFG3),
113                ag71xx_rr(ag, AG71XX_REG_FIFO_CFG4),
114                ag71xx_rr(ag, AG71XX_REG_FIFO_CFG5));
115
116        DBG("%s: mac_cfg2=%#x, mac_ifctl=%#x, mii_ctrl=%#x\n",
117                ag->dev->name,
118                ag71xx_rr(ag, AG71XX_REG_MAC_CFG2),
119                ag71xx_rr(ag, AG71XX_REG_MAC_IFCTL),
120                ag71xx_mii_ctrl_rr(ag));
121}
122
123static void ag71xx_phy_link_adjust(struct net_device *dev)
124{
125        struct ag71xx *ag = netdev_priv(dev);
126        struct phy_device *phydev = ag->phy_dev;
127        unsigned long flags;
128        int status_change = 0;
129
130        spin_lock_irqsave(&ag->lock, flags);
131
132        if (phydev->link) {
133                if (ag->duplex != phydev->duplex
134                    || ag->speed != phydev->speed) {
135                        status_change = 1;
136                }
137        }
138
139        if (phydev->link != ag->link) {
140                if (phydev->link)
141                        netif_schedule(dev);
142
143                status_change = 1;
144        }
145
146        ag->link = phydev->link;
147        ag->duplex = phydev->duplex;
148        ag->speed = phydev->speed;
149
150        if (status_change)
151                ag71xx_phy_link_update(ag);
152
153        spin_unlock_irqrestore(&ag->lock, flags);
154}
155
156void ag71xx_phy_start(struct ag71xx *ag)
157{
158        if (ag->phy_dev) {
159                phy_start(ag->phy_dev);
160        } else {
161                struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
162
163                ag->duplex = pdata->duplex;
164                ag->speed = pdata->speed;
165                ag->link = 1;
166                ag71xx_phy_link_update(ag);
167        }
168}
169
170void ag71xx_phy_stop(struct ag71xx *ag)
171{
172        if (ag->phy_dev) {
173                phy_stop(ag->phy_dev);
174        } else {
175                ag->duplex = -1;
176                ag->link = 0;
177                ag->speed = 0;
178                ag71xx_phy_link_update(ag);
179        }
180}
181
182int ag71xx_phy_connect(struct ag71xx *ag)
183{
184        struct net_device *dev = ag->dev;
185        struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
186        struct phy_device *phydev = NULL;
187        int phy_count = 0;
188        int phy_addr;
189
190        if (ag->mii_bus && pdata->phy_mask) {
191                /* TODO: use mutex of the mdio bus? */
192                for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
193                        if (!(pdata->phy_mask & (1 << phy_addr)))
194                                continue;
195
196                        if (ag->mii_bus->phy_map[phy_addr] == NULL)
197                                continue;
198
199                        DBG("%s: PHY found at %s, uid=%08x\n",
200                                dev->name,
201                                ag->mii_bus->phy_map[phy_addr]->dev.bus_id,
202                                ag->mii_bus->phy_map[phy_addr]->phy_id);
203
204                        if (phydev == NULL)
205                                phydev = ag->mii_bus->phy_map[phy_addr];
206
207                        phy_count++;
208                }
209        }
210
211        switch (phy_count) {
212        case 1:
213                ag->phy_dev = phy_connect(dev, phydev->dev.bus_id,
214                        &ag71xx_phy_link_adjust, 0, pdata->phy_if_mode);
215
216                if (IS_ERR(ag->phy_dev)) {
217                        printk(KERN_ERR "%s: could not connect to PHY at %s\n",
218                                dev->name, phydev->dev.bus_id);
219                        return PTR_ERR(ag->phy_dev);
220                }
221
222                /* mask with MAC supported features */
223                if (pdata->has_gbit)
224                        phydev->supported &= PHY_GBIT_FEATURES;
225                else
226                        phydev->supported &= PHY_BASIC_FEATURES;
227
228                phydev->advertising = phydev->supported;
229
230                printk(KERN_DEBUG "%s: connected to PHY at %s "
231                        "[uid=%08x, driver=%s]\n",
232                        dev->name, phydev->dev.bus_id,
233                        phydev->phy_id, phydev->drv->name);
234
235                ag->link = 0;
236                ag->speed = 0;
237                ag->duplex = -1;
238                break;
239
240        default:
241                switch (pdata->speed) {
242                case SPEED_10:
243                case SPEED_100:
244                case SPEED_1000:
245                        break;
246                default:
247                        printk(KERN_ERR "%s: invalid speed specified\n",
248                                dev->name);
249                        return -EINVAL;
250                }
251
252                ag->phy_dev = NULL;
253                printk(KERN_DEBUG "%s: connected to %d PHYs\n",
254                        dev->name, phy_count);
255                break;
256        }
257
258        return 0;
259}
260
261void ag71xx_phy_disconnect(struct ag71xx *ag)
262{
263        if (ag->phy_dev)
264                phy_disconnect(ag->phy_dev);
265}
Note: See TracBrowser for help on using the repository browser.