Skip to content

Commit caaa71f

Browse files
Ansueldavem330
authored andcommitted
net: mdio: add ipq8064 mdio driver
Currently ipq806x soc use generic bitbang driver to comunicate with the gmac ethernet interface. Add a dedicated driver created by chunkeey to fix this. Co-developed-by: Christian Lamparter <[email protected]> Signed-off-by: Christian Lamparter <[email protected]> Signed-off-by: Ansuel Smith <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 425c075 commit caaa71f

File tree

3 files changed

+175
-0
lines changed

3 files changed

+175
-0
lines changed

drivers/net/phy/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,14 @@ config MDIO_I2C
157157

158158
This is library mode.
159159

160+
config MDIO_IPQ8064
161+
tristate "Qualcomm IPQ8064 MDIO interface support"
162+
depends on HAS_IOMEM && OF_MDIO
163+
depends on MFD_SYSCON
164+
help
165+
This driver supports the MDIO interface found in the network
166+
interface units of the IPQ8064 SoC
167+
160168
config MDIO_MOXART
161169
tristate "MOXA ART MDIO interface support"
162170
depends on ARCH_MOXART || COMPILE_TEST

drivers/net/phy/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ obj-$(CONFIG_MDIO_CAVIUM) += mdio-cavium.o
3737
obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o
3838
obj-$(CONFIG_MDIO_HISI_FEMAC) += mdio-hisi-femac.o
3939
obj-$(CONFIG_MDIO_I2C) += mdio-i2c.o
40+
obj-$(CONFIG_MDIO_IPQ8064) += mdio-ipq8064.o
4041
obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o
4142
obj-$(CONFIG_MDIO_MSCC_MIIM) += mdio-mscc-miim.o
4243
obj-$(CONFIG_MDIO_OCTEON) += mdio-octeon.o

drivers/net/phy/mdio-ipq8064.c

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Qualcomm IPQ8064 MDIO interface driver
3+
*
4+
* Copyright (C) 2019 Christian Lamparter <[email protected]>
5+
* Copyright (C) 2020 Ansuel Smith <[email protected]>
6+
*/
7+
8+
#include <linux/delay.h>
9+
#include <linux/kernel.h>
10+
#include <linux/module.h>
11+
#include <linux/regmap.h>
12+
#include <linux/of_mdio.h>
13+
#include <linux/phy.h>
14+
#include <linux/platform_device.h>
15+
#include <linux/mfd/syscon.h>
16+
17+
/* MII address register definitions */
18+
#define MII_ADDR_REG_ADDR 0x10
19+
#define MII_BUSY BIT(0)
20+
#define MII_WRITE BIT(1)
21+
#define MII_CLKRANGE_60_100M (0 << 2)
22+
#define MII_CLKRANGE_100_150M (1 << 2)
23+
#define MII_CLKRANGE_20_35M (2 << 2)
24+
#define MII_CLKRANGE_35_60M (3 << 2)
25+
#define MII_CLKRANGE_150_250M (4 << 2)
26+
#define MII_CLKRANGE_250_300M (5 << 2)
27+
#define MII_CLKRANGE_MASK GENMASK(4, 2)
28+
#define MII_REG_SHIFT 6
29+
#define MII_REG_MASK GENMASK(10, 6)
30+
#define MII_ADDR_SHIFT 11
31+
#define MII_ADDR_MASK GENMASK(15, 11)
32+
33+
#define MII_DATA_REG_ADDR 0x14
34+
35+
#define MII_MDIO_DELAY_USEC (1000)
36+
#define MII_MDIO_RETRY_MSEC (10)
37+
38+
struct ipq8064_mdio {
39+
struct regmap *base; /* NSS_GMAC0_BASE */
40+
};
41+
42+
static int
43+
ipq8064_mdio_wait_busy(struct ipq8064_mdio *priv)
44+
{
45+
u32 busy;
46+
47+
return regmap_read_poll_timeout(priv->base, MII_ADDR_REG_ADDR, busy,
48+
!(busy & MII_BUSY), MII_MDIO_DELAY_USEC,
49+
MII_MDIO_RETRY_MSEC * USEC_PER_MSEC);
50+
}
51+
52+
static int
53+
ipq8064_mdio_read(struct mii_bus *bus, int phy_addr, int reg_offset)
54+
{
55+
u32 miiaddr = MII_BUSY | MII_CLKRANGE_250_300M;
56+
struct ipq8064_mdio *priv = bus->priv;
57+
u32 ret_val;
58+
int err;
59+
60+
/* Reject clause 45 */
61+
if (reg_offset & MII_ADDR_C45)
62+
return -EOPNOTSUPP;
63+
64+
miiaddr |= ((phy_addr << MII_ADDR_SHIFT) & MII_ADDR_MASK) |
65+
((reg_offset << MII_REG_SHIFT) & MII_REG_MASK);
66+
67+
regmap_write(priv->base, MII_ADDR_REG_ADDR, miiaddr);
68+
usleep_range(8, 10);
69+
70+
err = ipq8064_mdio_wait_busy(priv);
71+
if (err)
72+
return err;
73+
74+
regmap_read(priv->base, MII_DATA_REG_ADDR, &ret_val);
75+
return (int)ret_val;
76+
}
77+
78+
static int
79+
ipq8064_mdio_write(struct mii_bus *bus, int phy_addr, int reg_offset, u16 data)
80+
{
81+
u32 miiaddr = MII_WRITE | MII_BUSY | MII_CLKRANGE_250_300M;
82+
struct ipq8064_mdio *priv = bus->priv;
83+
84+
/* Reject clause 45 */
85+
if (reg_offset & MII_ADDR_C45)
86+
return -EOPNOTSUPP;
87+
88+
regmap_write(priv->base, MII_DATA_REG_ADDR, data);
89+
90+
miiaddr |= ((phy_addr << MII_ADDR_SHIFT) & MII_ADDR_MASK) |
91+
((reg_offset << MII_REG_SHIFT) & MII_REG_MASK);
92+
93+
regmap_write(priv->base, MII_ADDR_REG_ADDR, miiaddr);
94+
usleep_range(8, 10);
95+
96+
return ipq8064_mdio_wait_busy(priv);
97+
}
98+
99+
static int
100+
ipq8064_mdio_probe(struct platform_device *pdev)
101+
{
102+
struct device_node *np = pdev->dev.of_node;
103+
struct ipq8064_mdio *priv;
104+
struct mii_bus *bus;
105+
int ret;
106+
107+
bus = devm_mdiobus_alloc_size(&pdev->dev, sizeof(*priv));
108+
if (!bus)
109+
return -ENOMEM;
110+
111+
bus->name = "ipq8064_mdio_bus";
112+
bus->read = ipq8064_mdio_read;
113+
bus->write = ipq8064_mdio_write;
114+
snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&pdev->dev));
115+
bus->parent = &pdev->dev;
116+
117+
priv = bus->priv;
118+
priv->base = device_node_to_regmap(np);
119+
if (IS_ERR(priv->base)) {
120+
if (priv->base == ERR_PTR(-EPROBE_DEFER))
121+
return -EPROBE_DEFER;
122+
123+
dev_err(&pdev->dev, "error getting device regmap, error=%pe\n",
124+
priv->base);
125+
return PTR_ERR(priv->base);
126+
}
127+
128+
ret = of_mdiobus_register(bus, np);
129+
if (ret)
130+
return ret;
131+
132+
platform_set_drvdata(pdev, bus);
133+
return 0;
134+
}
135+
136+
static int
137+
ipq8064_mdio_remove(struct platform_device *pdev)
138+
{
139+
struct mii_bus *bus = platform_get_drvdata(pdev);
140+
141+
mdiobus_unregister(bus);
142+
143+
return 0;
144+
}
145+
146+
static const struct of_device_id ipq8064_mdio_dt_ids[] = {
147+
{ .compatible = "qcom,ipq8064-mdio" },
148+
{ }
149+
};
150+
MODULE_DEVICE_TABLE(of, ipq8064_mdio_dt_ids);
151+
152+
static struct platform_driver ipq8064_mdio_driver = {
153+
.probe = ipq8064_mdio_probe,
154+
.remove = ipq8064_mdio_remove,
155+
.driver = {
156+
.name = "ipq8064-mdio",
157+
.of_match_table = ipq8064_mdio_dt_ids,
158+
},
159+
};
160+
161+
module_platform_driver(ipq8064_mdio_driver);
162+
163+
MODULE_DESCRIPTION("Qualcomm IPQ8064 MDIO interface driver");
164+
MODULE_AUTHOR("Christian Lamparter <[email protected]>");
165+
MODULE_AUTHOR("Ansuel Smith <[email protected]>");
166+
MODULE_LICENSE("GPL");

0 commit comments

Comments
 (0)