2 * Copyright (C) 2015 Broadcom Corporation
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation version 2.
8 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
9 * kind, whether express or implied; without even the implied warranty
10 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
14 #include <linux/delay.h>
16 #include <linux/kernel.h>
17 #include <linux/module.h>
19 #include <linux/of_platform.h>
20 #include <linux/of_mdio.h>
21 #include <linux/phy.h>
22 #include <linux/platform_device.h>
23 #include <linux/sched.h>
25 #define IPROC_GPHY_MDCDIV 0x1a
27 #define MII_CTRL_OFFSET 0x000
29 #define MII_CTRL_DIV_SHIFT 0
30 #define MII_CTRL_PRE_SHIFT 7
31 #define MII_CTRL_BUSY_SHIFT 8
33 #define MII_DATA_OFFSET 0x004
34 #define MII_DATA_MASK 0xffff
35 #define MII_DATA_TA_SHIFT 16
36 #define MII_DATA_TA_VAL 2
37 #define MII_DATA_RA_SHIFT 18
38 #define MII_DATA_PA_SHIFT 23
39 #define MII_DATA_OP_SHIFT 28
40 #define MII_DATA_OP_WRITE 1
41 #define MII_DATA_OP_READ 2
42 #define MII_DATA_SB_SHIFT 30
44 struct iproc_mdio_priv
{
45 struct mii_bus
*mii_bus
;
49 static inline int iproc_mdio_wait_for_idle(void __iomem
*base
)
52 unsigned int timeout
= 1000; /* loop for 1s */
55 val
= readl(base
+ MII_CTRL_OFFSET
);
56 if ((val
& BIT(MII_CTRL_BUSY_SHIFT
)) == 0)
59 usleep_range(1000, 2000);
65 static inline void iproc_mdio_config_clk(void __iomem
*base
)
69 val
= (IPROC_GPHY_MDCDIV
<< MII_CTRL_DIV_SHIFT
) |
70 BIT(MII_CTRL_PRE_SHIFT
);
71 writel(val
, base
+ MII_CTRL_OFFSET
);
74 static int iproc_mdio_read(struct mii_bus
*bus
, int phy_id
, int reg
)
76 struct iproc_mdio_priv
*priv
= bus
->priv
;
80 rc
= iproc_mdio_wait_for_idle(priv
->base
);
84 iproc_mdio_config_clk(priv
->base
);
86 /* Prepare the read operation */
87 cmd
= (MII_DATA_TA_VAL
<< MII_DATA_TA_SHIFT
) |
88 (reg
<< MII_DATA_RA_SHIFT
) |
89 (phy_id
<< MII_DATA_PA_SHIFT
) |
90 BIT(MII_DATA_SB_SHIFT
) |
91 (MII_DATA_OP_READ
<< MII_DATA_OP_SHIFT
);
93 writel(cmd
, priv
->base
+ MII_DATA_OFFSET
);
95 rc
= iproc_mdio_wait_for_idle(priv
->base
);
99 cmd
= readl(priv
->base
+ MII_DATA_OFFSET
) & MII_DATA_MASK
;
104 static int iproc_mdio_write(struct mii_bus
*bus
, int phy_id
,
107 struct iproc_mdio_priv
*priv
= bus
->priv
;
111 rc
= iproc_mdio_wait_for_idle(priv
->base
);
115 iproc_mdio_config_clk(priv
->base
);
117 /* Prepare the write operation */
118 cmd
= (MII_DATA_TA_VAL
<< MII_DATA_TA_SHIFT
) |
119 (reg
<< MII_DATA_RA_SHIFT
) |
120 (phy_id
<< MII_DATA_PA_SHIFT
) |
121 BIT(MII_DATA_SB_SHIFT
) |
122 (MII_DATA_OP_WRITE
<< MII_DATA_OP_SHIFT
) |
123 ((u32
)(val
) & MII_DATA_MASK
);
125 writel(cmd
, priv
->base
+ MII_DATA_OFFSET
);
127 rc
= iproc_mdio_wait_for_idle(priv
->base
);
134 static int iproc_mdio_probe(struct platform_device
*pdev
)
136 struct iproc_mdio_priv
*priv
;
138 struct resource
*res
;
141 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
145 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
146 priv
->base
= devm_ioremap_resource(&pdev
->dev
, res
);
147 if (IS_ERR(priv
->base
)) {
148 dev_err(&pdev
->dev
, "failed to ioremap register\n");
149 return PTR_ERR(priv
->base
);
152 priv
->mii_bus
= mdiobus_alloc();
153 if (!priv
->mii_bus
) {
154 dev_err(&pdev
->dev
, "MDIO bus alloc failed\n");
160 bus
->name
= "iProc MDIO bus";
161 snprintf(bus
->id
, MII_BUS_ID_SIZE
, "%s-%d", pdev
->name
, pdev
->id
);
162 bus
->parent
= &pdev
->dev
;
163 bus
->read
= iproc_mdio_read
;
164 bus
->write
= iproc_mdio_write
;
166 rc
= of_mdiobus_register(bus
, pdev
->dev
.of_node
);
168 dev_err(&pdev
->dev
, "MDIO bus registration failed\n");
172 platform_set_drvdata(pdev
, priv
);
174 dev_info(&pdev
->dev
, "Broadcom iProc MDIO bus at 0x%p\n", priv
->base
);
183 static int iproc_mdio_remove(struct platform_device
*pdev
)
185 struct iproc_mdio_priv
*priv
= platform_get_drvdata(pdev
);
187 mdiobus_unregister(priv
->mii_bus
);
188 mdiobus_free(priv
->mii_bus
);
193 static const struct of_device_id iproc_mdio_of_match
[] = {
194 { .compatible
= "brcm,iproc-mdio", },
197 MODULE_DEVICE_TABLE(of
, iproc_mdio_of_match
);
199 static struct platform_driver iproc_mdio_driver
= {
201 .name
= "iproc-mdio",
202 .of_match_table
= iproc_mdio_of_match
,
204 .probe
= iproc_mdio_probe
,
205 .remove
= iproc_mdio_remove
,
208 module_platform_driver(iproc_mdio_driver
);
210 MODULE_AUTHOR("Broadcom Corporation");
211 MODULE_DESCRIPTION("Broadcom iProc MDIO bus controller");
212 MODULE_LICENSE("GPL v2");
213 MODULE_ALIAS("platform:iproc-mdio");