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>
16 #define REG_PHY_CTRL 0
17 #define REG_PHY_WRITE_DATA 4
20 #define MIIWR BIT(27) /* init write sequence (auto cleared)*/
22 #define REGAD_MASK 0x3e00000
23 #define PHYAD_MASK 0x1f0000
24 #define MIIRDATA_MASK 0xffff
26 /* REG_PHY_WRITE_DATA */
27 #define MIIWDATA_MASK 0xffff
29 struct moxart_mdio_data
{
33 static int moxart_mdio_read(struct mii_bus
*bus
, int mii_id
, int regnum
)
35 struct moxart_mdio_data
*data
= bus
->priv
;
37 unsigned int count
= 5;
39 dev_dbg(&bus
->dev
, "%s\n", __func__
);
41 ctrl
|= MIIRD
| ((mii_id
<< 16) & PHYAD_MASK
) |
42 ((regnum
<< 21) & REGAD_MASK
);
44 writel(ctrl
, data
->base
+ REG_PHY_CTRL
);
47 ctrl
= readl(data
->base
+ REG_PHY_CTRL
);
50 return ctrl
& MIIRDATA_MASK
;
56 dev_dbg(&bus
->dev
, "%s timed out\n", __func__
);
61 static int moxart_mdio_write(struct mii_bus
*bus
, int mii_id
,
62 int regnum
, u16 value
)
64 struct moxart_mdio_data
*data
= bus
->priv
;
66 unsigned int count
= 5;
68 dev_dbg(&bus
->dev
, "%s\n", __func__
);
70 ctrl
|= MIIWR
| ((mii_id
<< 16) & PHYAD_MASK
) |
71 ((regnum
<< 21) & REGAD_MASK
);
73 value
&= MIIWDATA_MASK
;
75 writel(value
, data
->base
+ REG_PHY_WRITE_DATA
);
76 writel(ctrl
, data
->base
+ REG_PHY_CTRL
);
79 ctrl
= readl(data
->base
+ REG_PHY_CTRL
);
88 dev_dbg(&bus
->dev
, "%s timed out\n", __func__
);
93 static int moxart_mdio_reset(struct mii_bus
*bus
)
97 for (i
= 0; i
< PHY_MAX_ADDR
; i
++) {
98 data
= moxart_mdio_read(bus
, i
, MII_BMCR
);
103 if (moxart_mdio_write(bus
, i
, MII_BMCR
, data
) < 0)
110 static int moxart_mdio_probe(struct platform_device
*pdev
)
112 struct device_node
*np
= pdev
->dev
.of_node
;
114 struct moxart_mdio_data
*data
;
117 bus
= mdiobus_alloc_size(sizeof(*data
));
121 bus
->name
= "MOXA ART Ethernet MII";
122 bus
->read
= &moxart_mdio_read
;
123 bus
->write
= &moxart_mdio_write
;
124 bus
->reset
= &moxart_mdio_reset
;
125 snprintf(bus
->id
, MII_BUS_ID_SIZE
, "%s-%d-mii", pdev
->name
, pdev
->id
);
126 bus
->parent
= &pdev
->dev
;
128 /* Setting PHY_MAC_INTERRUPT here even if it has no effect,
129 * of_mdiobus_register() sets these PHY_POLL.
130 * Ideally, the interrupt from MAC controller could be used to
131 * detect link state changes, not polling, i.e. if there was
132 * a way phy_driver could set PHY_HAS_INTERRUPT but have that
133 * interrupt handled in ethernet drivercode.
135 for (i
= 0; i
< PHY_MAX_ADDR
; i
++)
136 bus
->irq
[i
] = PHY_MAC_INTERRUPT
;
139 data
->base
= devm_platform_ioremap_resource(pdev
, 0);
140 if (IS_ERR(data
->base
)) {
141 ret
= PTR_ERR(data
->base
);
142 goto err_out_free_mdiobus
;
145 ret
= of_mdiobus_register(bus
, np
);
147 goto err_out_free_mdiobus
;
149 platform_set_drvdata(pdev
, bus
);
153 err_out_free_mdiobus
:
158 static void moxart_mdio_remove(struct platform_device
*pdev
)
160 struct mii_bus
*bus
= platform_get_drvdata(pdev
);
162 mdiobus_unregister(bus
);
166 static const struct of_device_id moxart_mdio_dt_ids
[] = {
167 { .compatible
= "moxa,moxart-mdio" },
170 MODULE_DEVICE_TABLE(of
, moxart_mdio_dt_ids
);
172 static struct platform_driver moxart_mdio_driver
= {
173 .probe
= moxart_mdio_probe
,
174 .remove
= moxart_mdio_remove
,
176 .name
= "moxart-mdio",
177 .of_match_table
= moxart_mdio_dt_ids
,
181 module_platform_driver(moxart_mdio_driver
);
183 MODULE_DESCRIPTION("MOXA ART MDIO interface driver");
184 MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
185 MODULE_LICENSE("GPL v2");