1 // SPDX-License-Identifier: GPL-2.0
2 /* Qualcomm IPQ8064 MDIO interface driver
4 * Copyright (C) 2019 Christian Lamparter <chunkeey@gmail.com>
5 * Copyright (C) 2020 Ansuel Smith <ansuelsmth@gmail.com>
8 #include <linux/delay.h>
9 #include <linux/kernel.h>
10 #include <linux/module.h>
11 #include <linux/of_mdio.h>
12 #include <linux/of_address.h>
13 #include <linux/platform_device.h>
14 #include <linux/regmap.h>
16 /* MII address register definitions */
17 #define MII_ADDR_REG_ADDR 0x10
18 #define MII_BUSY BIT(0)
19 #define MII_WRITE BIT(1)
20 #define MII_CLKRANGE(x) ((x) << 2)
21 #define MII_CLKRANGE_60_100M MII_CLKRANGE(0)
22 #define MII_CLKRANGE_100_150M MII_CLKRANGE(1)
23 #define MII_CLKRANGE_20_35M MII_CLKRANGE(2)
24 #define MII_CLKRANGE_35_60M MII_CLKRANGE(3)
25 #define MII_CLKRANGE_150_250M MII_CLKRANGE(4)
26 #define MII_CLKRANGE_250_300M MII_CLKRANGE(5)
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)
33 #define MII_DATA_REG_ADDR 0x14
35 #define MII_MDIO_DELAY_USEC (1000)
36 #define MII_MDIO_RETRY_MSEC (10)
39 struct regmap
*base
; /* NSS_GMAC0_BASE */
43 ipq8064_mdio_wait_busy(struct ipq8064_mdio
*priv
)
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
);
53 ipq8064_mdio_read(struct mii_bus
*bus
, int phy_addr
, int reg_offset
)
55 u32 miiaddr
= MII_BUSY
| MII_CLKRANGE_250_300M
;
56 struct ipq8064_mdio
*priv
= bus
->priv
;
60 miiaddr
|= ((phy_addr
<< MII_ADDR_SHIFT
) & MII_ADDR_MASK
) |
61 ((reg_offset
<< MII_REG_SHIFT
) & MII_REG_MASK
);
63 regmap_write(priv
->base
, MII_ADDR_REG_ADDR
, miiaddr
);
66 err
= ipq8064_mdio_wait_busy(priv
);
70 regmap_read(priv
->base
, MII_DATA_REG_ADDR
, &ret_val
);
75 ipq8064_mdio_write(struct mii_bus
*bus
, int phy_addr
, int reg_offset
, u16 data
)
77 u32 miiaddr
= MII_WRITE
| MII_BUSY
| MII_CLKRANGE_250_300M
;
78 struct ipq8064_mdio
*priv
= bus
->priv
;
80 regmap_write(priv
->base
, MII_DATA_REG_ADDR
, data
);
82 miiaddr
|= ((phy_addr
<< MII_ADDR_SHIFT
) & MII_ADDR_MASK
) |
83 ((reg_offset
<< MII_REG_SHIFT
) & MII_REG_MASK
);
85 regmap_write(priv
->base
, MII_ADDR_REG_ADDR
, miiaddr
);
87 /* For the specific reg 31 extra time is needed or the next
88 * read will produce garbage data.
95 return ipq8064_mdio_wait_busy(priv
);
98 static const struct regmap_config ipq8064_mdio_regmap_config
= {
102 .can_multi_write
= false,
103 /* the mdio lock is used by any user of this mdio driver */
104 .disable_locking
= true,
106 .cache_type
= REGCACHE_NONE
,
110 ipq8064_mdio_probe(struct platform_device
*pdev
)
112 struct device_node
*np
= pdev
->dev
.of_node
;
113 struct ipq8064_mdio
*priv
;
119 if (of_address_to_resource(np
, 0, &res
))
122 base
= devm_ioremap(&pdev
->dev
, res
.start
, resource_size(&res
));
126 bus
= devm_mdiobus_alloc_size(&pdev
->dev
, sizeof(*priv
));
130 bus
->name
= "ipq8064_mdio_bus";
131 bus
->read
= ipq8064_mdio_read
;
132 bus
->write
= ipq8064_mdio_write
;
133 snprintf(bus
->id
, MII_BUS_ID_SIZE
, "%s-mii", dev_name(&pdev
->dev
));
134 bus
->parent
= &pdev
->dev
;
137 priv
->base
= devm_regmap_init_mmio(&pdev
->dev
, base
,
138 &ipq8064_mdio_regmap_config
);
139 if (IS_ERR(priv
->base
))
140 return PTR_ERR(priv
->base
);
142 ret
= of_mdiobus_register(bus
, np
);
146 platform_set_drvdata(pdev
, bus
);
150 static void ipq8064_mdio_remove(struct platform_device
*pdev
)
152 struct mii_bus
*bus
= platform_get_drvdata(pdev
);
154 mdiobus_unregister(bus
);
157 static const struct of_device_id ipq8064_mdio_dt_ids
[] = {
158 { .compatible
= "qcom,ipq8064-mdio" },
161 MODULE_DEVICE_TABLE(of
, ipq8064_mdio_dt_ids
);
163 static struct platform_driver ipq8064_mdio_driver
= {
164 .probe
= ipq8064_mdio_probe
,
165 .remove
= ipq8064_mdio_remove
,
167 .name
= "ipq8064-mdio",
168 .of_match_table
= ipq8064_mdio_dt_ids
,
172 module_platform_driver(ipq8064_mdio_driver
);
174 MODULE_DESCRIPTION("Qualcomm IPQ8064 MDIO interface driver");
175 MODULE_AUTHOR("Christian Lamparter <chunkeey@gmail.com>");
176 MODULE_AUTHOR("Ansuel Smith <ansuelsmth@gmail.com>");
177 MODULE_LICENSE("GPL");