2 * Broadcom UniMAC MDIO bus controller driver
4 * Copyright (C) 2014, Broadcom Corporation
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
12 #include <linux/kernel.h>
13 #include <linux/phy.h>
14 #include <linux/platform_device.h>
15 #include <linux/sched.h>
16 #include <linux/module.h>
18 #include <linux/delay.h>
21 #include <linux/of_platform.h>
22 #include <linux/of_mdio.h>
25 #define MDIO_START_BUSY (1 << 29)
26 #define MDIO_READ_FAIL (1 << 28)
27 #define MDIO_RD (2 << 26)
28 #define MDIO_WR (1 << 26)
29 #define MDIO_PMD_SHIFT 21
30 #define MDIO_PMD_MASK 0x1F
31 #define MDIO_REG_SHIFT 16
32 #define MDIO_REG_MASK 0x1F
35 #define MDIO_C22 (1 << 0)
37 #define MDIO_CLK_DIV_SHIFT 4
38 #define MDIO_CLK_DIV_MASK 0x3F
39 #define MDIO_SUPP_PREAMBLE (1 << 12)
41 struct unimac_mdio_priv
{
42 struct mii_bus
*mii_bus
;
46 static inline void unimac_mdio_start(struct unimac_mdio_priv
*priv
)
50 reg
= __raw_readl(priv
->base
+ MDIO_CMD
);
51 reg
|= MDIO_START_BUSY
;
52 __raw_writel(reg
, priv
->base
+ MDIO_CMD
);
55 static inline unsigned int unimac_mdio_busy(struct unimac_mdio_priv
*priv
)
57 return __raw_readl(priv
->base
+ MDIO_CMD
) & MDIO_START_BUSY
;
60 static int unimac_mdio_read(struct mii_bus
*bus
, int phy_id
, int reg
)
62 struct unimac_mdio_priv
*priv
= bus
->priv
;
63 unsigned int timeout
= 1000;
66 /* Prepare the read operation */
67 cmd
= MDIO_RD
| (phy_id
<< MDIO_PMD_SHIFT
) | (reg
<< MDIO_REG_SHIFT
);
68 __raw_writel(cmd
, priv
->base
+ MDIO_CMD
);
70 /* Start MDIO transaction */
71 unimac_mdio_start(priv
);
74 if (!unimac_mdio_busy(priv
))
77 usleep_range(1000, 2000);
83 cmd
= __raw_readl(priv
->base
+ MDIO_CMD
);
85 /* Some broken devices are known not to release the line during
86 * turn-around, e.g: Broadcom BCM53125 external switches, so check for
87 * that condition here and ignore the MDIO controller read failure
90 if (!(bus
->phy_ignore_ta_mask
& 1 << phy_id
) && (cmd
& MDIO_READ_FAIL
))
96 static int unimac_mdio_write(struct mii_bus
*bus
, int phy_id
,
99 struct unimac_mdio_priv
*priv
= bus
->priv
;
100 unsigned int timeout
= 1000;
103 /* Prepare the write operation */
104 cmd
= MDIO_WR
| (phy_id
<< MDIO_PMD_SHIFT
) |
105 (reg
<< MDIO_REG_SHIFT
) | (0xffff & val
);
106 __raw_writel(cmd
, priv
->base
+ MDIO_CMD
);
108 unimac_mdio_start(priv
);
111 if (!unimac_mdio_busy(priv
))
114 usleep_range(1000, 2000);
123 static int unimac_mdio_probe(struct platform_device
*pdev
)
125 struct unimac_mdio_priv
*priv
;
126 struct device_node
*np
;
131 np
= pdev
->dev
.of_node
;
133 priv
= devm_kzalloc(&pdev
->dev
, sizeof(*priv
), GFP_KERNEL
);
137 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
139 /* Just ioremap, as this MDIO block is usually integrated into an
140 * Ethernet MAC controller register range
142 priv
->base
= devm_ioremap(&pdev
->dev
, r
->start
, resource_size(r
));
144 dev_err(&pdev
->dev
, "failed to remap register\n");
148 priv
->mii_bus
= mdiobus_alloc();
154 bus
->name
= "unimac MII bus";
155 bus
->parent
= &pdev
->dev
;
156 bus
->read
= unimac_mdio_read
;
157 bus
->write
= unimac_mdio_write
;
158 snprintf(bus
->id
, MII_BUS_ID_SIZE
, "%s", pdev
->name
);
160 bus
->irq
= kcalloc(PHY_MAX_ADDR
, sizeof(int), GFP_KERNEL
);
166 ret
= of_mdiobus_register(bus
, np
);
168 dev_err(&pdev
->dev
, "MDIO bus registration failed\n");
172 platform_set_drvdata(pdev
, priv
);
174 dev_info(&pdev
->dev
, "Broadcom UniMAC MDIO bus at 0x%p\n", priv
->base
);
185 static int unimac_mdio_remove(struct platform_device
*pdev
)
187 struct unimac_mdio_priv
*priv
= platform_get_drvdata(pdev
);
189 mdiobus_unregister(priv
->mii_bus
);
190 kfree(priv
->mii_bus
->irq
);
191 mdiobus_free(priv
->mii_bus
);
196 static const struct of_device_id unimac_mdio_ids
[] = {
197 { .compatible
= "brcm,genet-mdio-v4", },
198 { .compatible
= "brcm,genet-mdio-v3", },
199 { .compatible
= "brcm,genet-mdio-v2", },
200 { .compatible
= "brcm,genet-mdio-v1", },
201 { .compatible
= "brcm,unimac-mdio", },
205 static struct platform_driver unimac_mdio_driver
= {
207 .name
= "unimac-mdio",
208 .of_match_table
= unimac_mdio_ids
,
210 .probe
= unimac_mdio_probe
,
211 .remove
= unimac_mdio_remove
,
213 module_platform_driver(unimac_mdio_driver
);
215 MODULE_AUTHOR("Broadcom Corporation");
216 MODULE_DESCRIPTION("Broadcom UniMAC MDIO bus controller");
217 MODULE_LICENSE("GPL");
218 MODULE_ALIAS("platform:unimac-mdio");