• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 2009-2012 Cavium, Inc.
7  */
8 
9 #include <linux/platform_device.h>
10 #include <linux/of_mdio.h>
11 #include <linux/delay.h>
12 #include <linux/module.h>
13 #include <linux/gfp.h>
14 #include <linux/phy.h>
15 #include <linux/io.h>
16 
17 #include <asm/octeon/octeon.h>
18 #include <asm/octeon/cvmx-smix-defs.h>
19 
20 #define DRV_VERSION "1.0"
21 #define DRV_DESCRIPTION "Cavium Networks Octeon SMI/MDIO driver"
22 
23 #define SMI_CMD		0x0
24 #define SMI_WR_DAT	0x8
25 #define SMI_RD_DAT	0x10
26 #define SMI_CLK		0x18
27 #define SMI_EN		0x20
28 
29 enum octeon_mdiobus_mode {
30 	UNINIT = 0,
31 	C22,
32 	C45
33 };
34 
35 struct octeon_mdiobus {
36 	struct mii_bus *mii_bus;
37 	u64 register_base;
38 	resource_size_t mdio_phys;
39 	resource_size_t regsize;
40 	enum octeon_mdiobus_mode mode;
41 	int phy_irq[PHY_MAX_ADDR];
42 };
43 
octeon_mdiobus_set_mode(struct octeon_mdiobus * p,enum octeon_mdiobus_mode m)44 static void octeon_mdiobus_set_mode(struct octeon_mdiobus *p,
45 				    enum octeon_mdiobus_mode m)
46 {
47 	union cvmx_smix_clk smi_clk;
48 
49 	if (m == p->mode)
50 		return;
51 
52 	smi_clk.u64 = cvmx_read_csr(p->register_base + SMI_CLK);
53 	smi_clk.s.mode = (m == C45) ? 1 : 0;
54 	smi_clk.s.preamble = 1;
55 	cvmx_write_csr(p->register_base + SMI_CLK, smi_clk.u64);
56 	p->mode = m;
57 }
58 
octeon_mdiobus_c45_addr(struct octeon_mdiobus * p,int phy_id,int regnum)59 static int octeon_mdiobus_c45_addr(struct octeon_mdiobus *p,
60 				   int phy_id, int regnum)
61 {
62 	union cvmx_smix_cmd smi_cmd;
63 	union cvmx_smix_wr_dat smi_wr;
64 	int timeout = 1000;
65 
66 	octeon_mdiobus_set_mode(p, C45);
67 
68 	smi_wr.u64 = 0;
69 	smi_wr.s.dat = regnum & 0xffff;
70 	cvmx_write_csr(p->register_base + SMI_WR_DAT, smi_wr.u64);
71 
72 	regnum = (regnum >> 16) & 0x1f;
73 
74 	smi_cmd.u64 = 0;
75 	smi_cmd.s.phy_op = 0; /* MDIO_CLAUSE_45_ADDRESS */
76 	smi_cmd.s.phy_adr = phy_id;
77 	smi_cmd.s.reg_adr = regnum;
78 	cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64);
79 
80 	do {
81 		/* Wait 1000 clocks so we don't saturate the RSL bus
82 		 * doing reads.
83 		 */
84 		__delay(1000);
85 		smi_wr.u64 = cvmx_read_csr(p->register_base + SMI_WR_DAT);
86 	} while (smi_wr.s.pending && --timeout);
87 
88 	if (timeout <= 0)
89 		return -EIO;
90 	return 0;
91 }
92 
octeon_mdiobus_read(struct mii_bus * bus,int phy_id,int regnum)93 static int octeon_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)
94 {
95 	struct octeon_mdiobus *p = bus->priv;
96 	union cvmx_smix_cmd smi_cmd;
97 	union cvmx_smix_rd_dat smi_rd;
98 	unsigned int op = 1; /* MDIO_CLAUSE_22_READ */
99 	int timeout = 1000;
100 
101 	if (regnum & MII_ADDR_C45) {
102 		int r = octeon_mdiobus_c45_addr(p, phy_id, regnum);
103 		if (r < 0)
104 			return r;
105 
106 		regnum = (regnum >> 16) & 0x1f;
107 		op = 3; /* MDIO_CLAUSE_45_READ */
108 	} else {
109 		octeon_mdiobus_set_mode(p, C22);
110 	}
111 
112 
113 	smi_cmd.u64 = 0;
114 	smi_cmd.s.phy_op = op;
115 	smi_cmd.s.phy_adr = phy_id;
116 	smi_cmd.s.reg_adr = regnum;
117 	cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64);
118 
119 	do {
120 		/* Wait 1000 clocks so we don't saturate the RSL bus
121 		 * doing reads.
122 		 */
123 		__delay(1000);
124 		smi_rd.u64 = cvmx_read_csr(p->register_base + SMI_RD_DAT);
125 	} while (smi_rd.s.pending && --timeout);
126 
127 	if (smi_rd.s.val)
128 		return smi_rd.s.dat;
129 	else
130 		return -EIO;
131 }
132 
octeon_mdiobus_write(struct mii_bus * bus,int phy_id,int regnum,u16 val)133 static int octeon_mdiobus_write(struct mii_bus *bus, int phy_id,
134 				int regnum, u16 val)
135 {
136 	struct octeon_mdiobus *p = bus->priv;
137 	union cvmx_smix_cmd smi_cmd;
138 	union cvmx_smix_wr_dat smi_wr;
139 	unsigned int op = 0; /* MDIO_CLAUSE_22_WRITE */
140 	int timeout = 1000;
141 
142 
143 	if (regnum & MII_ADDR_C45) {
144 		int r = octeon_mdiobus_c45_addr(p, phy_id, regnum);
145 		if (r < 0)
146 			return r;
147 
148 		regnum = (regnum >> 16) & 0x1f;
149 		op = 1; /* MDIO_CLAUSE_45_WRITE */
150 	} else {
151 		octeon_mdiobus_set_mode(p, C22);
152 	}
153 
154 	smi_wr.u64 = 0;
155 	smi_wr.s.dat = val;
156 	cvmx_write_csr(p->register_base + SMI_WR_DAT, smi_wr.u64);
157 
158 	smi_cmd.u64 = 0;
159 	smi_cmd.s.phy_op = op;
160 	smi_cmd.s.phy_adr = phy_id;
161 	smi_cmd.s.reg_adr = regnum;
162 	cvmx_write_csr(p->register_base + SMI_CMD, smi_cmd.u64);
163 
164 	do {
165 		/* Wait 1000 clocks so we don't saturate the RSL bus
166 		 * doing reads.
167 		 */
168 		__delay(1000);
169 		smi_wr.u64 = cvmx_read_csr(p->register_base + SMI_WR_DAT);
170 	} while (smi_wr.s.pending && --timeout);
171 
172 	if (timeout <= 0)
173 		return -EIO;
174 
175 	return 0;
176 }
177 
octeon_mdiobus_probe(struct platform_device * pdev)178 static int octeon_mdiobus_probe(struct platform_device *pdev)
179 {
180 	struct octeon_mdiobus *bus;
181 	struct resource *res_mem;
182 	union cvmx_smix_en smi_en;
183 	int err = -ENOENT;
184 
185 	bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
186 	if (!bus)
187 		return -ENOMEM;
188 
189 	res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
190 
191 	if (res_mem == NULL) {
192 		dev_err(&pdev->dev, "found no memory resource\n");
193 		err = -ENXIO;
194 		goto fail;
195 	}
196 	bus->mdio_phys = res_mem->start;
197 	bus->regsize = resource_size(res_mem);
198 	if (!devm_request_mem_region(&pdev->dev, bus->mdio_phys, bus->regsize,
199 				     res_mem->name)) {
200 		dev_err(&pdev->dev, "request_mem_region failed\n");
201 		goto fail;
202 	}
203 	bus->register_base =
204 		(u64)devm_ioremap(&pdev->dev, bus->mdio_phys, bus->regsize);
205 
206 	bus->mii_bus = mdiobus_alloc();
207 
208 	if (!bus->mii_bus)
209 		goto fail;
210 
211 	smi_en.u64 = 0;
212 	smi_en.s.en = 1;
213 	cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64);
214 
215 	bus->mii_bus->priv = bus;
216 	bus->mii_bus->irq = bus->phy_irq;
217 	bus->mii_bus->name = "mdio-octeon";
218 	snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%llx", bus->register_base);
219 	bus->mii_bus->parent = &pdev->dev;
220 
221 	bus->mii_bus->read = octeon_mdiobus_read;
222 	bus->mii_bus->write = octeon_mdiobus_write;
223 
224 	platform_set_drvdata(pdev, bus);
225 
226 	err = of_mdiobus_register(bus->mii_bus, pdev->dev.of_node);
227 	if (err)
228 		goto fail_register;
229 
230 	dev_info(&pdev->dev, "Version " DRV_VERSION "\n");
231 
232 	return 0;
233 fail_register:
234 	mdiobus_free(bus->mii_bus);
235 fail:
236 	smi_en.u64 = 0;
237 	cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64);
238 	return err;
239 }
240 
octeon_mdiobus_remove(struct platform_device * pdev)241 static int octeon_mdiobus_remove(struct platform_device *pdev)
242 {
243 	struct octeon_mdiobus *bus;
244 	union cvmx_smix_en smi_en;
245 
246 	bus = platform_get_drvdata(pdev);
247 
248 	mdiobus_unregister(bus->mii_bus);
249 	mdiobus_free(bus->mii_bus);
250 	smi_en.u64 = 0;
251 	cvmx_write_csr(bus->register_base + SMI_EN, smi_en.u64);
252 	return 0;
253 }
254 
255 static struct of_device_id octeon_mdiobus_match[] = {
256 	{
257 		.compatible = "cavium,octeon-3860-mdio",
258 	},
259 	{},
260 };
261 MODULE_DEVICE_TABLE(of, octeon_mdiobus_match);
262 
263 static struct platform_driver octeon_mdiobus_driver = {
264 	.driver = {
265 		.name		= "mdio-octeon",
266 		.owner		= THIS_MODULE,
267 		.of_match_table = octeon_mdiobus_match,
268 	},
269 	.probe		= octeon_mdiobus_probe,
270 	.remove		= octeon_mdiobus_remove,
271 };
272 
octeon_mdiobus_force_mod_depencency(void)273 void octeon_mdiobus_force_mod_depencency(void)
274 {
275 	/* Let ethernet drivers force us to be loaded.  */
276 }
277 EXPORT_SYMBOL(octeon_mdiobus_force_mod_depencency);
278 
279 module_platform_driver(octeon_mdiobus_driver);
280 
281 MODULE_DESCRIPTION(DRV_DESCRIPTION);
282 MODULE_VERSION(DRV_VERSION);
283 MODULE_AUTHOR("David Daney");
284 MODULE_LICENSE("GPL");
285