• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7800-V1.0.2.28/target/linux/ar71xx/files/drivers/net/ethernet/atheros/ag71xx/
1/*
2 *  Atheros AR71xx built-in ethernet mac driver
3 *
4 *  Copyright (c) 2013 The Linux Foundation. All rights reserved.
5 *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
6 *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
7 *
8 *  Based on Atheros' AG7100 driver
9 *
10 *  This program is free software; you can redistribute it and/or modify it
11 *  under the terms of the GNU General Public License version 2 as published
12 *  by the Free Software Foundation.
13 */
14
15#include "ag71xx.h"
16#ifdef CONFIG_OF
17#include <linux/of.h>
18#endif
19
20#define AG71XX_MDIO_RETRY	1000
21#define AG71XX_MDIO_DELAY	5
22
23static inline void ag71xx_mdio_wr(struct ag71xx_mdio *am, unsigned reg,
24				  u32 value)
25{
26	void __iomem *r;
27
28	r = am->mdio_base + reg;
29	__raw_writel(value, r);
30
31	/* flush write */
32	(void) __raw_readl(r);
33}
34
35static inline u32 ag71xx_mdio_rr(struct ag71xx_mdio *am, unsigned reg)
36{
37	return __raw_readl(am->mdio_base + reg);
38}
39
40static void ag71xx_mdio_dump_regs(struct ag71xx_mdio *am)
41{
42	DBG("%s: mii_cfg=%08x, mii_cmd=%08x, mii_addr=%08x\n",
43		am->mii_bus->name,
44		ag71xx_mdio_rr(am, AG71XX_REG_MII_CFG),
45		ag71xx_mdio_rr(am, AG71XX_REG_MII_CMD),
46		ag71xx_mdio_rr(am, AG71XX_REG_MII_ADDR));
47	DBG("%s: mii_ctrl=%08x, mii_status=%08x, mii_ind=%08x\n",
48		am->mii_bus->name,
49		ag71xx_mdio_rr(am, AG71XX_REG_MII_CTRL),
50		ag71xx_mdio_rr(am, AG71XX_REG_MII_STATUS),
51		ag71xx_mdio_rr(am, AG71XX_REG_MII_IND));
52}
53
54static int ag71xx_mdio_wait_busy(struct ag71xx_mdio *am)
55{
56	int i;
57
58	for (i = 0; i < AG71XX_MDIO_RETRY; i++) {
59		u32 busy;
60
61		udelay(AG71XX_MDIO_DELAY);
62
63		busy = ag71xx_mdio_rr(am, AG71XX_REG_MII_IND);
64		if (!busy)
65			return 0;
66
67		udelay(AG71XX_MDIO_DELAY);
68	}
69
70	pr_err("%s: MDIO operation timed out\n", am->mii_bus->name);
71
72	return -ETIMEDOUT;
73}
74
75int ag71xx_mdio_mii_read(struct ag71xx_mdio *am, int addr, int reg)
76{
77	int err;
78	int ret;
79
80	err = ag71xx_mdio_wait_busy(am);
81	if (err)
82		return 0xffff;
83
84	ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
85	ag71xx_mdio_wr(am, AG71XX_REG_MII_ADDR,
86			((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff));
87	ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_READ);
88
89	err = ag71xx_mdio_wait_busy(am);
90	if (err)
91		return 0xffff;
92
93	ret = ag71xx_mdio_rr(am, AG71XX_REG_MII_STATUS) & 0xffff;
94	ag71xx_mdio_wr(am, AG71XX_REG_MII_CMD, MII_CMD_WRITE);
95
96	DBG("mii_read: addr=%04x, reg=%04x, value=%04x\n", addr, reg, ret);
97
98	return ret;
99}
100
101void ag71xx_mdio_mii_write(struct ag71xx_mdio *am, int addr, int reg, u16 val)
102{
103	DBG("mii_write: addr=%04x, reg=%04x, value=%04x\n", addr, reg, val);
104
105	ag71xx_mdio_wr(am, AG71XX_REG_MII_ADDR,
106			((addr & 0xff) << MII_ADDR_SHIFT) | (reg & 0xff));
107	ag71xx_mdio_wr(am, AG71XX_REG_MII_CTRL, val);
108
109	ag71xx_mdio_wait_busy(am);
110}
111
112static int ag71xx_mdio_reset(struct mii_bus *bus)
113{
114	struct ag71xx_mdio *am = bus->priv;
115	u32 t;
116
117	if (am->pdata->is_ar7240)
118		t = MII_CFG_CLK_DIV_6;
119	else if (am->pdata->builtin_switch && !am->pdata->is_ar934x)
120		t = MII_CFG_CLK_DIV_10;
121	else if (!am->pdata->builtin_switch && am->pdata->is_ar934x)
122		t = MII_CFG_CLK_DIV_58;
123	else
124		t = MII_CFG_CLK_DIV_28;
125
126	ag71xx_mdio_wr(am, AG71XX_REG_MII_CFG, t | MII_CFG_RESET);
127	udelay(100);
128
129	ag71xx_mdio_wr(am, AG71XX_REG_MII_CFG, t);
130	udelay(100);
131
132	return 0;
133}
134
135static int ag71xx_mdio_read(struct mii_bus *bus, int addr, int reg)
136{
137	struct ag71xx_mdio *am = bus->priv;
138
139	if (am->pdata->builtin_switch)
140		return ar7240sw_phy_read(bus, addr, reg);
141	else
142		return ag71xx_mdio_mii_read(am, addr, reg);
143}
144
145static int ag71xx_mdio_write(struct mii_bus *bus, int addr, int reg, u16 val)
146{
147	struct ag71xx_mdio *am = bus->priv;
148
149	if (am->pdata->builtin_switch)
150		ar7240sw_phy_write(bus, addr, reg, val);
151	else
152		ag71xx_mdio_mii_write(am, addr, reg, val);
153	return 0;
154}
155
156static int __devinit ag71xx_mdio_probe(struct platform_device *pdev)
157{
158	struct ag71xx_mdio_platform_data *pdata;
159	struct ag71xx_mdio *am;
160	struct resource *res;
161	int i;
162	int err;
163
164	pdata = pdev->dev.platform_data;
165	if (!pdata) {
166		dev_err(&pdev->dev, "no platform data specified\n");
167		return -EINVAL;
168	}
169
170	am = kzalloc(sizeof(*am), GFP_KERNEL);
171	if (!am) {
172		err = -ENOMEM;
173		goto err_out;
174	}
175
176	am->pdata = pdata;
177
178	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
179	if (!res) {
180		dev_err(&pdev->dev, "no iomem resource found\n");
181		err = -ENXIO;
182		goto err_out;
183	}
184
185	am->mdio_base = ioremap_nocache(res->start, res->end - res->start + 1);
186	if (!am->mdio_base) {
187		dev_err(&pdev->dev, "unable to ioremap registers\n");
188		err = -ENOMEM;
189		goto err_free_mdio;
190	}
191
192	am->mii_bus = mdiobus_alloc();
193	if (am->mii_bus == NULL) {
194		err = -ENOMEM;
195		goto err_iounmap;
196	}
197
198	am->mii_bus->name = "ag71xx_mdio";
199	am->mii_bus->read = ag71xx_mdio_read;
200	am->mii_bus->write = ag71xx_mdio_write;
201	am->mii_bus->reset = ag71xx_mdio_reset;
202	am->mii_bus->irq = am->mii_irq;
203	am->mii_bus->priv = am;
204	am->mii_bus->parent = &pdev->dev;
205	snprintf(am->mii_bus->id, MII_BUS_ID_SIZE, "%s", dev_name(&pdev->dev));
206	am->mii_bus->phy_mask = pdata->phy_mask;
207
208	for (i = 0; i < PHY_MAX_ADDR; i++)
209		am->mii_irq[i] = PHY_POLL;
210
211	ag71xx_mdio_wr(am, AG71XX_REG_MAC_CFG1, 0);
212
213	err = mdiobus_register(am->mii_bus);
214	if (err)
215		goto err_free_bus;
216
217	ag71xx_mdio_dump_regs(am);
218
219	platform_set_drvdata(pdev, am);
220	return 0;
221
222err_free_bus:
223	mdiobus_free(am->mii_bus);
224err_iounmap:
225	iounmap(am->mdio_base);
226err_free_mdio:
227	kfree(am);
228err_out:
229	return err;
230}
231
232static int __devexit ag71xx_mdio_remove(struct platform_device *pdev)
233{
234	struct ag71xx_mdio *am = platform_get_drvdata(pdev);
235
236	if (am) {
237		mdiobus_unregister(am->mii_bus);
238		mdiobus_free(am->mii_bus);
239		iounmap(am->mdio_base);
240		kfree(am);
241		platform_set_drvdata(pdev, NULL);
242	}
243
244	return 0;
245}
246
247#ifdef CONFIG_OF
248static const struct of_device_id ag71xx_of_match_table[] = {
249	{.compatible = "qcom,ag71xx-mdio"},
250	{}
251};
252#else
253#define ag71xx_of_match_table NULL
254#endif
255
256static struct platform_driver ag71xx_mdio_driver = {
257	.probe		= ag71xx_mdio_probe,
258	.remove		= __exit_p(ag71xx_mdio_remove),
259	.driver = {
260		.name	= "ag71xx-mdio",
261		.of_match_table = ag71xx_of_match_table,
262	}
263};
264
265int __init ag71xx_mdio_driver_init(void)
266{
267	return platform_driver_register(&ag71xx_mdio_driver);
268}
269
270void ag71xx_mdio_driver_exit(void)
271{
272	platform_driver_unregister(&ag71xx_mdio_driver);
273}
274