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
6 * Copyright (C) 2009,2011 Cavium, Inc.
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/init.h>
14 #include <linux/gfp.h>
15 #include <linux/phy.h>
18 #include <asm/octeon/octeon.h>
19 #include <asm/octeon/cvmx-smix-defs.h>
21 #define DRV_VERSION "1.0"
22 #define DRV_DESCRIPTION "Cavium Networks Octeon SMI/MDIO driver"
25 #define SMI_WR_DAT 0x8
26 #define SMI_RD_DAT 0x10
30 struct octeon_mdiobus
{
31 struct mii_bus
*mii_bus
;
33 resource_size_t mdio_phys
;
34 resource_size_t regsize
;
35 int phy_irq
[PHY_MAX_ADDR
];
38 static int octeon_mdiobus_read(struct mii_bus
*bus
, int phy_id
, int regnum
)
40 struct octeon_mdiobus
*p
= bus
->priv
;
41 union cvmx_smix_cmd smi_cmd
;
42 union cvmx_smix_rd_dat smi_rd
;
46 smi_cmd
.s
.phy_op
= 1; /* MDIO_CLAUSE_22_READ */
47 smi_cmd
.s
.phy_adr
= phy_id
;
48 smi_cmd
.s
.reg_adr
= regnum
;
49 cvmx_write_csr(p
->register_base
+ SMI_CMD
, smi_cmd
.u64
);
53 * Wait 1000 clocks so we don't saturate the RSL bus
57 smi_rd
.u64
= cvmx_read_csr(p
->register_base
+ SMI_RD_DAT
);
58 } while (smi_rd
.s
.pending
&& --timeout
);
66 static int octeon_mdiobus_write(struct mii_bus
*bus
, int phy_id
,
69 struct octeon_mdiobus
*p
= bus
->priv
;
70 union cvmx_smix_cmd smi_cmd
;
71 union cvmx_smix_wr_dat smi_wr
;
76 cvmx_write_csr(p
->register_base
+ SMI_WR_DAT
, smi_wr
.u64
);
79 smi_cmd
.s
.phy_op
= 0; /* MDIO_CLAUSE_22_WRITE */
80 smi_cmd
.s
.phy_adr
= phy_id
;
81 smi_cmd
.s
.reg_adr
= regnum
;
82 cvmx_write_csr(p
->register_base
+ SMI_CMD
, smi_cmd
.u64
);
86 * Wait 1000 clocks so we don't saturate the RSL bus
90 smi_wr
.u64
= cvmx_read_csr(p
->register_base
+ SMI_WR_DAT
);
91 } while (smi_wr
.s
.pending
&& --timeout
);
99 static int __devinit
octeon_mdiobus_probe(struct platform_device
*pdev
)
101 struct octeon_mdiobus
*bus
;
102 struct resource
*res_mem
;
103 union cvmx_smix_en smi_en
;
106 bus
= devm_kzalloc(&pdev
->dev
, sizeof(*bus
), GFP_KERNEL
);
110 res_mem
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
112 if (res_mem
== NULL
) {
113 dev_err(&pdev
->dev
, "found no memory resource\n");
117 bus
->mdio_phys
= res_mem
->start
;
118 bus
->regsize
= resource_size(res_mem
);
119 if (!devm_request_mem_region(&pdev
->dev
, bus
->mdio_phys
, bus
->regsize
,
121 dev_err(&pdev
->dev
, "request_mem_region failed\n");
125 (u64
)devm_ioremap(&pdev
->dev
, bus
->mdio_phys
, bus
->regsize
);
127 bus
->mii_bus
= mdiobus_alloc();
134 cvmx_write_csr(bus
->register_base
+ SMI_EN
, smi_en
.u64
);
136 bus
->mii_bus
->priv
= bus
;
137 bus
->mii_bus
->irq
= bus
->phy_irq
;
138 bus
->mii_bus
->name
= "mdio-octeon";
139 snprintf(bus
->mii_bus
->id
, MII_BUS_ID_SIZE
, "%llx", bus
->register_base
);
140 bus
->mii_bus
->parent
= &pdev
->dev
;
142 bus
->mii_bus
->read
= octeon_mdiobus_read
;
143 bus
->mii_bus
->write
= octeon_mdiobus_write
;
145 dev_set_drvdata(&pdev
->dev
, bus
);
147 err
= of_mdiobus_register(bus
->mii_bus
, pdev
->dev
.of_node
);
151 dev_info(&pdev
->dev
, "Version " DRV_VERSION
"\n");
155 mdiobus_free(bus
->mii_bus
);
158 cvmx_write_csr(bus
->register_base
+ SMI_EN
, smi_en
.u64
);
162 static int __devexit
octeon_mdiobus_remove(struct platform_device
*pdev
)
164 struct octeon_mdiobus
*bus
;
165 union cvmx_smix_en smi_en
;
167 bus
= dev_get_drvdata(&pdev
->dev
);
169 mdiobus_unregister(bus
->mii_bus
);
170 mdiobus_free(bus
->mii_bus
);
172 cvmx_write_csr(bus
->register_base
+ SMI_EN
, smi_en
.u64
);
176 static struct of_device_id octeon_mdiobus_match
[] = {
178 .compatible
= "cavium,octeon-3860-mdio",
182 MODULE_DEVICE_TABLE(of
, octeon_mdiobus_match
);
184 static struct platform_driver octeon_mdiobus_driver
= {
186 .name
= "mdio-octeon",
187 .owner
= THIS_MODULE
,
188 .of_match_table
= octeon_mdiobus_match
,
190 .probe
= octeon_mdiobus_probe
,
191 .remove
= __devexit_p(octeon_mdiobus_remove
),
194 void octeon_mdiobus_force_mod_depencency(void)
196 /* Let ethernet drivers force us to be loaded. */
198 EXPORT_SYMBOL(octeon_mdiobus_force_mod_depencency
);
200 static int __init
octeon_mdiobus_mod_init(void)
202 return platform_driver_register(&octeon_mdiobus_driver
);
205 static void __exit
octeon_mdiobus_mod_exit(void)
207 platform_driver_unregister(&octeon_mdiobus_driver
);
210 module_init(octeon_mdiobus_mod_init
);
211 module_exit(octeon_mdiobus_mod_exit
);
213 MODULE_DESCRIPTION(DRV_DESCRIPTION
);
214 MODULE_VERSION(DRV_VERSION
);
215 MODULE_AUTHOR("David Daney");
216 MODULE_LICENSE("GPL");