1 // SPDX-License-Identifier: GPL-2.0
2 /* MOXA ART Ethernet (RTL8201CP) MDIO interface driver
4 * Copyright (C) 2013 Jonas Jensen <jonas.jensen@gmail.com>
7 #include <linux/delay.h>
8 #include <linux/kernel.h>
9 #include <linux/module.h>
10 #include <linux/mutex.h>
11 #include <linux/of_address.h>
12 #include <linux/of_mdio.h>
13 #include <linux/phy.h>
14 #include <linux/platform_device.h>
15 #include <linux/regulator/consumer.h>
17 #define REG_PHY_CTRL 0
18 #define REG_PHY_WRITE_DATA 4
21 #define MIIWR BIT(27) /* init write sequence (auto cleared)*/
23 #define REGAD_MASK 0x3e00000
24 #define PHYAD_MASK 0x1f0000
25 #define MIIRDATA_MASK 0xffff
27 /* REG_PHY_WRITE_DATA */
28 #define MIIWDATA_MASK 0xffff
30 struct moxart_mdio_data
{
34 static int moxart_mdio_read(struct mii_bus
*bus
, int mii_id
, int regnum
)
36 struct moxart_mdio_data
*data
= bus
->priv
;
38 unsigned int count
= 5;
40 dev_dbg(&bus
->dev
, "%s\n", __func__
);
42 ctrl
|= MIIRD
| ((mii_id
<< 16) & PHYAD_MASK
) |
43 ((regnum
<< 21) & REGAD_MASK
);
45 writel(ctrl
, data
->base
+ REG_PHY_CTRL
);
48 ctrl
= readl(data
->base
+ REG_PHY_CTRL
);
51 return ctrl
& MIIRDATA_MASK
;
57 dev_dbg(&bus
->dev
, "%s timed out\n", __func__
);
62 static int moxart_mdio_write(struct mii_bus
*bus
, int mii_id
,
63 int regnum
, u16 value
)
65 struct moxart_mdio_data
*data
= bus
->priv
;
67 unsigned int count
= 5;
69 dev_dbg(&bus
->dev
, "%s\n", __func__
);
71 ctrl
|= MIIWR
| ((mii_id
<< 16) & PHYAD_MASK
) |
72 ((regnum
<< 21) & REGAD_MASK
);
74 value
&= MIIWDATA_MASK
;
76 writel(value
, data
->base
+ REG_PHY_WRITE_DATA
);
77 writel(ctrl
, data
->base
+ REG_PHY_CTRL
);
80 ctrl
= readl(data
->base
+ REG_PHY_CTRL
);
89 dev_dbg(&bus
->dev
, "%s timed out\n", __func__
);
94 static int moxart_mdio_reset(struct mii_bus
*bus
)
98 for (i
= 0; i
< PHY_MAX_ADDR
; i
++) {
99 data
= moxart_mdio_read(bus
, i
, MII_BMCR
);
104 if (moxart_mdio_write(bus
, i
, MII_BMCR
, data
) < 0)
111 static int moxart_mdio_probe(struct platform_device
*pdev
)
113 struct device_node
*np
= pdev
->dev
.of_node
;
115 struct moxart_mdio_data
*data
;
118 bus
= mdiobus_alloc_size(sizeof(*data
));
122 bus
->name
= "MOXA ART Ethernet MII";
123 bus
->read
= &moxart_mdio_read
;
124 bus
->write
= &moxart_mdio_write
;
125 bus
->reset
= &moxart_mdio_reset
;
126 snprintf(bus
->id
, MII_BUS_ID_SIZE
, "%s-%d-mii", pdev
->name
, pdev
->id
);
127 bus
->parent
= &pdev
->dev
;
129 /* Setting PHY_IGNORE_INTERRUPT here even if it has no effect,
130 * of_mdiobus_register() sets these PHY_POLL.
131 * Ideally, the interrupt from MAC controller could be used to
132 * detect link state changes, not polling, i.e. if there was
133 * a way phy_driver could set PHY_HAS_INTERRUPT but have that
134 * interrupt handled in ethernet drivercode.
136 for (i
= 0; i
< PHY_MAX_ADDR
; i
++)
137 bus
->irq
[i
] = PHY_IGNORE_INTERRUPT
;
140 data
->base
= devm_platform_ioremap_resource(pdev
, 0);
141 if (IS_ERR(data
->base
)) {
142 ret
= PTR_ERR(data
->base
);
143 goto err_out_free_mdiobus
;
146 ret
= of_mdiobus_register(bus
, np
);
148 goto err_out_free_mdiobus
;
150 platform_set_drvdata(pdev
, bus
);
154 err_out_free_mdiobus
:
159 static int moxart_mdio_remove(struct platform_device
*pdev
)
161 struct mii_bus
*bus
= platform_get_drvdata(pdev
);
163 mdiobus_unregister(bus
);
169 static const struct of_device_id moxart_mdio_dt_ids
[] = {
170 { .compatible
= "moxa,moxart-mdio" },
173 MODULE_DEVICE_TABLE(of
, moxart_mdio_dt_ids
);
175 static struct platform_driver moxart_mdio_driver
= {
176 .probe
= moxart_mdio_probe
,
177 .remove
= moxart_mdio_remove
,
179 .name
= "moxart-mdio",
180 .of_match_table
= moxart_mdio_dt_ids
,
184 module_platform_driver(moxart_mdio_driver
);
186 MODULE_DESCRIPTION("MOXA ART MDIO interface driver");
187 MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
188 MODULE_LICENSE("GPL v2");