xref: /linux/drivers/net/mdio/mdio-pic64hpsc.c (revision 91a4855d6c03e770e42f17c798a36a3c46e63de2)
1*f76aef98SCharles Perry // SPDX-License-Identifier: GPL-2.0
2*f76aef98SCharles Perry /* Microchip PIC64-HPSC/HX MDIO controller driver
3*f76aef98SCharles Perry  *
4*f76aef98SCharles Perry  * Copyright (c) 2026 Microchip Technology Inc. and its subsidiaries.
5*f76aef98SCharles Perry  */
6*f76aef98SCharles Perry 
7*f76aef98SCharles Perry #include <linux/bitops.h>
8*f76aef98SCharles Perry #include <linux/clk.h>
9*f76aef98SCharles Perry #include <linux/io.h>
10*f76aef98SCharles Perry #include <linux/iopoll.h>
11*f76aef98SCharles Perry #include <linux/kernel.h>
12*f76aef98SCharles Perry #include <linux/module.h>
13*f76aef98SCharles Perry #include <linux/of_mdio.h>
14*f76aef98SCharles Perry #include <linux/platform_device.h>
15*f76aef98SCharles Perry 
16*f76aef98SCharles Perry #define MDIO_REG_PRESCALER     0x20
17*f76aef98SCharles Perry #define MDIO_CFG_PRESCALE_MASK GENMASK(7, 0)
18*f76aef98SCharles Perry 
19*f76aef98SCharles Perry #define MDIO_REG_FRAME_CFG_1 0x24
20*f76aef98SCharles Perry #define MDIO_WDATA_MASK	     GENMASK(15, 0)
21*f76aef98SCharles Perry 
22*f76aef98SCharles Perry #define MDIO_REG_FRAME_CFG_2	 0x28
23*f76aef98SCharles Perry #define MDIO_TRIGGER_BIT	 BIT(31)
24*f76aef98SCharles Perry #define MDIO_REG_DEV_ADDR_MASK	 GENMASK(20, 16)
25*f76aef98SCharles Perry #define MDIO_PHY_PRT_ADDR_MASK	 GENMASK(8, 4)
26*f76aef98SCharles Perry #define MDIO_OPERATION_MASK	 GENMASK(3, 2)
27*f76aef98SCharles Perry #define MDIO_START_OF_FRAME_MASK GENMASK(1, 0)
28*f76aef98SCharles Perry 
29*f76aef98SCharles Perry /* Possible value of MDIO_OPERATION_MASK */
30*f76aef98SCharles Perry #define MDIO_OPERATION_WRITE BIT(0)
31*f76aef98SCharles Perry #define MDIO_OPERATION_READ  BIT(1)
32*f76aef98SCharles Perry 
33*f76aef98SCharles Perry #define MDIO_REG_FRAME_STATUS 0x2C
34*f76aef98SCharles Perry #define MDIO_READOK_BIT	      BIT(24)
35*f76aef98SCharles Perry #define MDIO_RDATA_MASK	      GENMASK(15, 0)
36*f76aef98SCharles Perry 
37*f76aef98SCharles Perry struct pic64hpsc_mdio_dev {
38*f76aef98SCharles Perry 	void __iomem *regs;
39*f76aef98SCharles Perry };
40*f76aef98SCharles Perry 
41*f76aef98SCharles Perry static int pic64hpsc_mdio_wait_trigger(struct mii_bus *bus)
42*f76aef98SCharles Perry {
43*f76aef98SCharles Perry 	struct pic64hpsc_mdio_dev *priv = bus->priv;
44*f76aef98SCharles Perry 	u32 val;
45*f76aef98SCharles Perry 	int ret;
46*f76aef98SCharles Perry 
47*f76aef98SCharles Perry 	/* The MDIO_TRIGGER bit returns 0 when a transaction has completed. */
48*f76aef98SCharles Perry 	ret = readl_poll_timeout(priv->regs + MDIO_REG_FRAME_CFG_2, val,
49*f76aef98SCharles Perry 				 !(val & MDIO_TRIGGER_BIT), 50, 10000);
50*f76aef98SCharles Perry 
51*f76aef98SCharles Perry 	if (ret < 0)
52*f76aef98SCharles Perry 		dev_dbg(&bus->dev, "TRIGGER bit timeout: %x\n", val);
53*f76aef98SCharles Perry 
54*f76aef98SCharles Perry 	return ret;
55*f76aef98SCharles Perry }
56*f76aef98SCharles Perry 
57*f76aef98SCharles Perry static int pic64hpsc_mdio_c22_read(struct mii_bus *bus, int mii_id, int regnum)
58*f76aef98SCharles Perry {
59*f76aef98SCharles Perry 	struct pic64hpsc_mdio_dev *priv = bus->priv;
60*f76aef98SCharles Perry 	u32 val;
61*f76aef98SCharles Perry 	int ret;
62*f76aef98SCharles Perry 
63*f76aef98SCharles Perry 	ret = pic64hpsc_mdio_wait_trigger(bus);
64*f76aef98SCharles Perry 	if (ret)
65*f76aef98SCharles Perry 		return ret;
66*f76aef98SCharles Perry 
67*f76aef98SCharles Perry 	writel(MDIO_TRIGGER_BIT | FIELD_PREP(MDIO_REG_DEV_ADDR_MASK, regnum) |
68*f76aef98SCharles Perry 		       FIELD_PREP(MDIO_PHY_PRT_ADDR_MASK, mii_id) |
69*f76aef98SCharles Perry 		       FIELD_PREP(MDIO_OPERATION_MASK, MDIO_OPERATION_READ) |
70*f76aef98SCharles Perry 		       FIELD_PREP(MDIO_START_OF_FRAME_MASK, 1),
71*f76aef98SCharles Perry 	       priv->regs + MDIO_REG_FRAME_CFG_2);
72*f76aef98SCharles Perry 
73*f76aef98SCharles Perry 	ret = pic64hpsc_mdio_wait_trigger(bus);
74*f76aef98SCharles Perry 	if (ret)
75*f76aef98SCharles Perry 		return ret;
76*f76aef98SCharles Perry 
77*f76aef98SCharles Perry 	val = readl(priv->regs + MDIO_REG_FRAME_STATUS);
78*f76aef98SCharles Perry 
79*f76aef98SCharles Perry 	/* The MDIO_READOK is a 1-bit value reflecting the inverse of the MDIO
80*f76aef98SCharles Perry 	 * bus value captured during the 2nd TA cycle. A PHY/Port should drive
81*f76aef98SCharles Perry 	 * the MDIO bus with a logic 0 on the 2nd TA cycle, however, the
82*f76aef98SCharles Perry 	 * PHY/Port could optionally drive a logic 1, to communicate a read
83*f76aef98SCharles Perry 	 * failure. This feature is optional, not defined by the 802.3 standard
84*f76aef98SCharles Perry 	 * and not supported in standard external PHYs.
85*f76aef98SCharles Perry 	 */
86*f76aef98SCharles Perry 	if (!(bus->phy_ignore_ta_mask & 1 << mii_id) &&
87*f76aef98SCharles Perry 	    !FIELD_GET(MDIO_READOK_BIT, val)) {
88*f76aef98SCharles Perry 		dev_dbg(&bus->dev, "READOK bit cleared\n");
89*f76aef98SCharles Perry 		return -EIO;
90*f76aef98SCharles Perry 	}
91*f76aef98SCharles Perry 
92*f76aef98SCharles Perry 	return FIELD_GET(MDIO_RDATA_MASK, val);
93*f76aef98SCharles Perry }
94*f76aef98SCharles Perry 
95*f76aef98SCharles Perry static int pic64hpsc_mdio_c22_write(struct mii_bus *bus, int mii_id, int regnum,
96*f76aef98SCharles Perry 				    u16 value)
97*f76aef98SCharles Perry {
98*f76aef98SCharles Perry 	struct pic64hpsc_mdio_dev *priv = bus->priv;
99*f76aef98SCharles Perry 	int ret;
100*f76aef98SCharles Perry 
101*f76aef98SCharles Perry 	ret = pic64hpsc_mdio_wait_trigger(bus);
102*f76aef98SCharles Perry 	if (ret < 0)
103*f76aef98SCharles Perry 		return ret;
104*f76aef98SCharles Perry 
105*f76aef98SCharles Perry 	writel(FIELD_PREP(MDIO_WDATA_MASK, value),
106*f76aef98SCharles Perry 	       priv->regs + MDIO_REG_FRAME_CFG_1);
107*f76aef98SCharles Perry 
108*f76aef98SCharles Perry 	writel(MDIO_TRIGGER_BIT | FIELD_PREP(MDIO_REG_DEV_ADDR_MASK, regnum) |
109*f76aef98SCharles Perry 		       FIELD_PREP(MDIO_PHY_PRT_ADDR_MASK, mii_id) |
110*f76aef98SCharles Perry 		       FIELD_PREP(MDIO_OPERATION_MASK, MDIO_OPERATION_WRITE) |
111*f76aef98SCharles Perry 		       FIELD_PREP(MDIO_START_OF_FRAME_MASK, 1),
112*f76aef98SCharles Perry 	       priv->regs + MDIO_REG_FRAME_CFG_2);
113*f76aef98SCharles Perry 
114*f76aef98SCharles Perry 	return 0;
115*f76aef98SCharles Perry }
116*f76aef98SCharles Perry 
117*f76aef98SCharles Perry static int pic64hpsc_mdio_probe(struct platform_device *pdev)
118*f76aef98SCharles Perry {
119*f76aef98SCharles Perry 	struct device_node *np = pdev->dev.of_node;
120*f76aef98SCharles Perry 	struct device *dev = &pdev->dev;
121*f76aef98SCharles Perry 	struct pic64hpsc_mdio_dev *priv;
122*f76aef98SCharles Perry 	struct mii_bus *bus;
123*f76aef98SCharles Perry 	unsigned long rate;
124*f76aef98SCharles Perry 	struct clk *clk;
125*f76aef98SCharles Perry 	u32 bus_freq;
126*f76aef98SCharles Perry 	u32 div;
127*f76aef98SCharles Perry 	int ret;
128*f76aef98SCharles Perry 
129*f76aef98SCharles Perry 	bus = devm_mdiobus_alloc_size(dev, sizeof(*priv));
130*f76aef98SCharles Perry 	if (!bus)
131*f76aef98SCharles Perry 		return -ENOMEM;
132*f76aef98SCharles Perry 
133*f76aef98SCharles Perry 	priv = bus->priv;
134*f76aef98SCharles Perry 
135*f76aef98SCharles Perry 	priv->regs = devm_platform_ioremap_resource(pdev, 0);
136*f76aef98SCharles Perry 	if (IS_ERR(priv->regs))
137*f76aef98SCharles Perry 		return PTR_ERR(priv->regs);
138*f76aef98SCharles Perry 
139*f76aef98SCharles Perry 	bus->name = KBUILD_MODNAME;
140*f76aef98SCharles Perry 	bus->read = pic64hpsc_mdio_c22_read;
141*f76aef98SCharles Perry 	bus->write = pic64hpsc_mdio_c22_write;
142*f76aef98SCharles Perry 	snprintf(bus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
143*f76aef98SCharles Perry 	bus->parent = dev;
144*f76aef98SCharles Perry 
145*f76aef98SCharles Perry 	clk = devm_clk_get_enabled(dev, NULL);
146*f76aef98SCharles Perry 	if (IS_ERR(clk))
147*f76aef98SCharles Perry 		return PTR_ERR(clk);
148*f76aef98SCharles Perry 
149*f76aef98SCharles Perry 	if (of_property_read_u32(np, "clock-frequency", &bus_freq))
150*f76aef98SCharles Perry 		bus_freq = 2500000;
151*f76aef98SCharles Perry 
152*f76aef98SCharles Perry 	rate = clk_get_rate(clk);
153*f76aef98SCharles Perry 
154*f76aef98SCharles Perry 	div = DIV_ROUND_UP(rate, 2 * bus_freq) - 1;
155*f76aef98SCharles Perry 	if (div == 0 || div & ~MDIO_CFG_PRESCALE_MASK) {
156*f76aef98SCharles Perry 		dev_err(dev, "MDIO clock-frequency out of range\n");
157*f76aef98SCharles Perry 		return -EINVAL;
158*f76aef98SCharles Perry 	}
159*f76aef98SCharles Perry 
160*f76aef98SCharles Perry 	dev_dbg(dev, "rate=%lu bus_freq=%u real_bus_freq=%lu div=%u\n", rate,
161*f76aef98SCharles Perry 		bus_freq, rate / (2 * (1 + div)), div);
162*f76aef98SCharles Perry 	writel(div, priv->regs + MDIO_REG_PRESCALER);
163*f76aef98SCharles Perry 
164*f76aef98SCharles Perry 	ret = devm_of_mdiobus_register(dev, bus, np);
165*f76aef98SCharles Perry 	if (ret) {
166*f76aef98SCharles Perry 		dev_err(dev, "Cannot register MDIO bus (%d)\n", ret);
167*f76aef98SCharles Perry 		return ret;
168*f76aef98SCharles Perry 	}
169*f76aef98SCharles Perry 
170*f76aef98SCharles Perry 	return 0;
171*f76aef98SCharles Perry }
172*f76aef98SCharles Perry 
173*f76aef98SCharles Perry static const struct of_device_id pic64hpsc_mdio_match[] = {
174*f76aef98SCharles Perry 	{ .compatible = "microchip,pic64hpsc-mdio" },
175*f76aef98SCharles Perry 	{}
176*f76aef98SCharles Perry };
177*f76aef98SCharles Perry MODULE_DEVICE_TABLE(of, pic64hpsc_mdio_match);
178*f76aef98SCharles Perry 
179*f76aef98SCharles Perry static struct platform_driver pic64hpsc_mdio_driver = {
180*f76aef98SCharles Perry 	.probe = pic64hpsc_mdio_probe,
181*f76aef98SCharles Perry 	.driver = {
182*f76aef98SCharles Perry 		.name = KBUILD_MODNAME,
183*f76aef98SCharles Perry 		.of_match_table = pic64hpsc_mdio_match,
184*f76aef98SCharles Perry 	},
185*f76aef98SCharles Perry };
186*f76aef98SCharles Perry module_platform_driver(pic64hpsc_mdio_driver);
187*f76aef98SCharles Perry 
188*f76aef98SCharles Perry MODULE_AUTHOR("Charles Perry <charles.perry@microchip.com>");
189*f76aef98SCharles Perry MODULE_DESCRIPTION("Microchip PIC64-HPSC/HX MDIO driver");
190*f76aef98SCharles Perry MODULE_LICENSE("GPL");
191