aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/phy
diff options
context:
space:
mode:
authorPramod Kumar <pramod.kumar@broadcom.com>2016-06-10 01:33:49 -0400
committerDavid S. Miller <davem@davemloft.net>2016-06-11 02:24:54 -0400
commit98bc865a1ec8074defd168b0feb9c466eeaeff33 (patch)
treefaf8991117596607edb0b48a0562cc664ad4d8c1 /drivers/net/phy
parent5f1a067bfa0ad29f944f772fbfd9ec7806260b54 (diff)
net: mdio-mux: Add MDIO mux driver for iProc SoCs
iProc based SoCs supports the integrated mdio multiplexer which has the bus selection as well as mdio transaction generation logic inside. This multiplexer has child buses for PCIe, SATA, USB and ETH. These buses could be internal or external to SOC where PHYs are attached. These buses could use C-45 or C-22 mdio transaction. Signed-off-by: Pramod Kumar <pramod.kumar@broadcom.com> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/Kconfig11
-rw-r--r--drivers/net/phy/Makefile1
-rw-r--r--drivers/net/phy/mdio-mux-bcm-iproc.c248
3 files changed, 260 insertions, 0 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 37d40c14ee13..8dac88abbc39 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -254,6 +254,17 @@ config MDIO_BUS_MUX_MMIOREG
254 254
255 Currently, only 8-bit registers are supported. 255 Currently, only 8-bit registers are supported.
256 256
257config MDIO_BUS_MUX_BCM_IPROC
258 tristate "Support for iProc based MDIO bus multiplexers"
259 depends on OF && OF_MDIO && (ARCH_BCM_IPROC || COMPILE_TEST)
260 select MDIO_BUS_MUX
261 default ARCH_BCM_IPROC
262 help
263 This module provides a driver for MDIO bus multiplexers found in
264 iProc based Broadcom SoCs. This multiplexer connects one of several
265 child MDIO bus to a parent bus. Buses could be internal as well as
266 external and selection logic lies inside the same multiplexer.
267
257config MDIO_BCM_UNIMAC 268config MDIO_BCM_UNIMAC
258 tristate "Broadcom UniMAC MDIO bus controller" 269 tristate "Broadcom UniMAC MDIO bus controller"
259 depends on HAS_IOMEM 270 depends on HAS_IOMEM
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index c26b651d0971..4170642a2035 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_AMD_PHY) += amd.o
39obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o 39obj-$(CONFIG_MDIO_BUS_MUX) += mdio-mux.o
40obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o 40obj-$(CONFIG_MDIO_BUS_MUX_GPIO) += mdio-mux-gpio.o
41obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o 41obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o
42obj-$(CONFIG_MDIO_BUS_MUX_BCM_IPROC) += mdio-mux-bcm-iproc.o
42obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o 43obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
43obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o 44obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o
44obj-$(CONFIG_MDIO_BCM_UNIMAC) += mdio-bcm-unimac.o 45obj-$(CONFIG_MDIO_BCM_UNIMAC) += mdio-bcm-unimac.o
diff --git a/drivers/net/phy/mdio-mux-bcm-iproc.c b/drivers/net/phy/mdio-mux-bcm-iproc.c
new file mode 100644
index 000000000000..0a0412524cec
--- /dev/null
+++ b/drivers/net/phy/mdio-mux-bcm-iproc.c
@@ -0,0 +1,248 @@
1/*
2 * Copyright 2016 Broadcom
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2, as
6 * published by the Free Software Foundation (the "GPL").
7 *
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License version 2 (GPLv2) for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * version 2 (GPLv2) along with this source code.
15 */
16
17#include <linux/platform_device.h>
18#include <linux/device.h>
19#include <linux/of_mdio.h>
20#include <linux/module.h>
21#include <linux/phy.h>
22#include <linux/mdio-mux.h>
23#include <linux/delay.h>
24
25#define MDIO_PARAM_OFFSET 0x00
26#define MDIO_PARAM_MIIM_CYCLE 29
27#define MDIO_PARAM_INTERNAL_SEL 25
28#define MDIO_PARAM_BUS_ID 22
29#define MDIO_PARAM_C45_SEL 21
30#define MDIO_PARAM_PHY_ID 16
31#define MDIO_PARAM_PHY_DATA 0
32
33#define MDIO_READ_OFFSET 0x04
34#define MDIO_READ_DATA_MASK 0xffff
35#define MDIO_ADDR_OFFSET 0x08
36
37#define MDIO_CTRL_OFFSET 0x0C
38#define MDIO_CTRL_WRITE_OP 0x1
39#define MDIO_CTRL_READ_OP 0x2
40
41#define MDIO_STAT_OFFSET 0x10
42#define MDIO_STAT_DONE 1
43
44#define BUS_MAX_ADDR 32
45#define EXT_BUS_START_ADDR 16
46
47struct iproc_mdiomux_desc {
48 void *mux_handle;
49 void __iomem *base;
50 struct device *dev;
51 struct mii_bus *mii_bus;
52};
53
54static int iproc_mdio_wait_for_idle(void __iomem *base, bool result)
55{
56 unsigned int timeout = 1000; /* loop for 1s */
57 u32 val;
58
59 do {
60 val = readl(base + MDIO_STAT_OFFSET);
61 if ((val & MDIO_STAT_DONE) == result)
62 return 0;
63
64 usleep_range(1000, 2000);
65 } while (timeout--);
66
67 return -ETIMEDOUT;
68}
69
70/* start_miim_ops- Program and start MDIO transaction over mdio bus.
71 * @base: Base address
72 * @phyid: phyid of the selected bus.
73 * @reg: register offset to be read/written.
74 * @val :0 if read op else value to be written in @reg;
75 * @op: Operation that need to be carried out.
76 * MDIO_CTRL_READ_OP: Read transaction.
77 * MDIO_CTRL_WRITE_OP: Write transaction.
78 *
79 * Return value: Successful Read operation returns read reg values and write
80 * operation returns 0. Failure operation returns negative error code.
81 */
82static int start_miim_ops(void __iomem *base,
83 u16 phyid, u32 reg, u16 val, u32 op)
84{
85 u32 param;
86 int ret;
87
88 writel(0, base + MDIO_CTRL_OFFSET);
89 ret = iproc_mdio_wait_for_idle(base, 0);
90 if (ret)
91 goto err;
92
93 param = readl(base + MDIO_PARAM_OFFSET);
94 param |= phyid << MDIO_PARAM_PHY_ID;
95 param |= val << MDIO_PARAM_PHY_DATA;
96 if (reg & MII_ADDR_C45)
97 param |= BIT(MDIO_PARAM_C45_SEL);
98
99 writel(param, base + MDIO_PARAM_OFFSET);
100
101 writel(reg, base + MDIO_ADDR_OFFSET);
102
103 writel(op, base + MDIO_CTRL_OFFSET);
104
105 ret = iproc_mdio_wait_for_idle(base, 1);
106 if (ret)
107 goto err;
108
109 if (op == MDIO_CTRL_READ_OP)
110 ret = readl(base + MDIO_READ_OFFSET) & MDIO_READ_DATA_MASK;
111err:
112 return ret;
113}
114
115static int iproc_mdiomux_read(struct mii_bus *bus, int phyid, int reg)
116{
117 struct iproc_mdiomux_desc *md = bus->priv;
118 int ret;
119
120 ret = start_miim_ops(md->base, phyid, reg, 0, MDIO_CTRL_READ_OP);
121 if (ret < 0)
122 dev_err(&bus->dev, "mdiomux read operation failed!!!");
123
124 return ret;
125}
126
127static int iproc_mdiomux_write(struct mii_bus *bus,
128 int phyid, int reg, u16 val)
129{
130 struct iproc_mdiomux_desc *md = bus->priv;
131 int ret;
132
133 /* Write val at reg offset */
134 ret = start_miim_ops(md->base, phyid, reg, val, MDIO_CTRL_WRITE_OP);
135 if (ret < 0)
136 dev_err(&bus->dev, "mdiomux write operation failed!!!");
137
138 return ret;
139}
140
141static int mdio_mux_iproc_switch_fn(int current_child, int desired_child,
142 void *data)
143{
144 struct iproc_mdiomux_desc *md = data;
145 u32 param, bus_id;
146 bool bus_dir;
147
148 /* select bus and its properties */
149 bus_dir = (desired_child < EXT_BUS_START_ADDR);
150 bus_id = bus_dir ? desired_child : (desired_child - EXT_BUS_START_ADDR);
151
152 param = (bus_dir ? 1 : 0) << MDIO_PARAM_INTERNAL_SEL;
153 param |= (bus_id << MDIO_PARAM_BUS_ID);
154
155 writel(param, md->base + MDIO_PARAM_OFFSET);
156 return 0;
157}
158
159static int mdio_mux_iproc_probe(struct platform_device *pdev)
160{
161 struct iproc_mdiomux_desc *md;
162 struct mii_bus *bus;
163 struct resource *res;
164 int rc;
165
166 md = devm_kzalloc(&pdev->dev, sizeof(*md), GFP_KERNEL);
167 if (!md)
168 return -ENOMEM;
169 md->dev = &pdev->dev;
170
171 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
172 md->base = devm_ioremap_resource(&pdev->dev, res);
173 if (IS_ERR(md->base)) {
174 dev_err(&pdev->dev, "failed to ioremap register\n");
175 return PTR_ERR(md->base);
176 }
177
178 md->mii_bus = mdiobus_alloc();
179 if (!md->mii_bus) {
180 dev_err(&pdev->dev, "mdiomux bus alloc failed\n");
181 return -ENOMEM;
182 }
183
184 bus = md->mii_bus;
185 bus->priv = md;
186 bus->name = "iProc MDIO mux bus";
187 snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id);
188 bus->parent = &pdev->dev;
189 bus->read = iproc_mdiomux_read;
190 bus->write = iproc_mdiomux_write;
191
192 bus->phy_mask = ~0;
193 bus->dev.of_node = pdev->dev.of_node;
194 rc = mdiobus_register(bus);
195 if (rc) {
196 dev_err(&pdev->dev, "mdiomux registration failed\n");
197 goto out;
198 }
199
200 platform_set_drvdata(pdev, md);
201
202 rc = mdio_mux_init(md->dev, mdio_mux_iproc_switch_fn,
203 &md->mux_handle, md, md->mii_bus);
204 if (rc) {
205 dev_info(md->dev, "mdiomux initialization failed\n");
206 goto out;
207 }
208
209 dev_info(md->dev, "iProc mdiomux registered\n");
210 return 0;
211out:
212 mdiobus_free(bus);
213 return rc;
214}
215
216static int mdio_mux_iproc_remove(struct platform_device *pdev)
217{
218 struct iproc_mdiomux_desc *md = dev_get_platdata(&pdev->dev);
219
220 mdio_mux_uninit(md->mux_handle);
221 mdiobus_unregister(md->mii_bus);
222 mdiobus_free(md->mii_bus);
223
224 return 0;
225}
226
227static const struct of_device_id mdio_mux_iproc_match[] = {
228 {
229 .compatible = "brcm,mdio-mux-iproc",
230 },
231 {},
232};
233MODULE_DEVICE_TABLE(of, mdio_mux_iproc_match);
234
235static struct platform_driver mdiomux_iproc_driver = {
236 .driver = {
237 .name = "mdio-mux-iproc",
238 .of_match_table = mdio_mux_iproc_match,
239 },
240 .probe = mdio_mux_iproc_probe,
241 .remove = mdio_mux_iproc_remove,
242};
243
244module_platform_driver(mdiomux_iproc_driver);
245
246MODULE_DESCRIPTION("iProc MDIO Mux Bus Driver");
247MODULE_AUTHOR("Pramod Kumar <pramod.kumar@broadcom.com>");
248MODULE_LICENSE("GPL v2");