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 /* Prepare the read operation */
85 cmd
= (MII_DATA_TA_VAL
<< MII_DATA_TA_SHIFT
) |
86 (reg
<< MII_DATA_RA_SHIFT
) |
87 (phy_id
<< MII_DATA_PA_SHIFT
) |
88 BIT(MII_DATA_SB_SHIFT
) |
89 (MII_DATA_OP_READ
<< MII_DATA_OP_SHIFT
);
91 writel(cmd
, priv
->base
+ MII_DATA_OFFSET
);
93 rc
= iproc_mdio_wait_for_idle(priv
->base
);
97 cmd
= readl(priv
->base
+ MII_DATA_OFFSET
) & MII_DATA_MASK
;
102 static int iproc_mdio_write(struct mii_bus
*bus
, int phy_id
,
105 struct iproc_mdio_priv
*priv
= bus
->priv
;
109 rc
= iproc_mdio_wait_for_idle(priv
->base
);
113 /* Prepare the write operation */
114 cmd
= (MII_DATA_TA_VAL
<< MII_DATA_TA_SHIFT
) |
115 (reg
<< MII_DATA_RA_SHIFT
) |
116 (phy_id
<< MII_DATA_PA_SHIFT
) |
117 BIT(MII_DATA_SB_SHIFT
) |
118 (MII_DATA_OP_WRITE
<< MII_DATA_OP_SHIFT
) |
119 ((u32
)(val
) & MII_DATA_MASK
);
121 writel(cmd
, priv
->base
+ MII_DATA_OFFSET
);
123 rc
= iproc_mdio_wait_for_idle(priv
->base
);
130 static int iproc_mdio_probe(struct platform_device
*pdev
)
132 struct iproc_mdio_priv
*priv
;
134 struct resource
*res
;
137 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
141 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
142 priv
->base
= devm_ioremap_resource(&pdev
->dev
, res
);
143 if (IS_ERR(priv
->base
)) {
144 dev_err(&pdev
->dev
, "failed to ioremap register\n");
145 return PTR_ERR(priv
->base
);
148 priv
->mii_bus
= mdiobus_alloc();
149 if (!priv
->mii_bus
) {
150 dev_err(&pdev
->dev
, "MDIO bus alloc failed\n");
156 bus
->name
= "iProc MDIO bus";
157 snprintf(bus
->id
, MII_BUS_ID_SIZE
, "%s-%d", pdev
->name
, pdev
->id
);
158 bus
->parent
= &pdev
->dev
;
159 bus
->read
= iproc_mdio_read
;
160 bus
->write
= iproc_mdio_write
;
162 iproc_mdio_config_clk(priv
->base
);
164 rc
= of_mdiobus_register(bus
, pdev
->dev
.of_node
);
166 dev_err(&pdev
->dev
, "MDIO bus registration failed\n");
170 platform_set_drvdata(pdev
, priv
);
172 dev_info(&pdev
->dev
, "Broadcom iProc MDIO bus at 0x%p\n", priv
->base
);
181 static int iproc_mdio_remove(struct platform_device
*pdev
)
183 struct iproc_mdio_priv
*priv
= platform_get_drvdata(pdev
);
185 mdiobus_unregister(priv
->mii_bus
);
186 mdiobus_free(priv
->mii_bus
);
191 static const struct of_device_id iproc_mdio_of_match
[] = {
192 { .compatible
= "brcm,iproc-mdio", },
195 MODULE_DEVICE_TABLE(of
, iproc_mdio_of_match
);
197 static struct platform_driver iproc_mdio_driver
= {
199 .name
= "iproc-mdio",
200 .of_match_table
= iproc_mdio_of_match
,
202 .probe
= iproc_mdio_probe
,
203 .remove
= iproc_mdio_remove
,
206 module_platform_driver(iproc_mdio_driver
);
208 MODULE_AUTHOR("Broadcom Corporation");
209 MODULE_DESCRIPTION("Broadcom iProc MDIO bus controller");
210 MODULE_LICENSE("GPL v2");
211 MODULE_ALIAS("platform:iproc-mdio");