1 /* MOXA ART Ethernet (RTL8201CP) MDIO interface driver
3 * Copyright (C) 2013 Jonas Jensen <jonas.jensen@gmail.com>
5 * This file is licensed under the terms of the GNU General Public
6 * License version 2. This program is licensed "as is" without any
7 * warranty of any kind, whether express or implied.
10 #include <linux/delay.h>
11 #include <linux/kernel.h>
12 #include <linux/module.h>
13 #include <linux/mutex.h>
14 #include <linux/of_address.h>
15 #include <linux/of_mdio.h>
16 #include <linux/phy.h>
17 #include <linux/platform_device.h>
18 #include <linux/regulator/consumer.h>
20 #define REG_PHY_CTRL 0
21 #define REG_PHY_WRITE_DATA 4
24 #define MIIWR BIT(27) /* init write sequence (auto cleared)*/
26 #define REGAD_MASK 0x3e00000
27 #define PHYAD_MASK 0x1f0000
28 #define MIIRDATA_MASK 0xffff
30 /* REG_PHY_WRITE_DATA */
31 #define MIIWDATA_MASK 0xffff
33 struct moxart_mdio_data
{
37 static int moxart_mdio_read(struct mii_bus
*bus
, int mii_id
, int regnum
)
39 struct moxart_mdio_data
*data
= bus
->priv
;
41 unsigned int count
= 5;
43 dev_dbg(&bus
->dev
, "%s\n", __func__
);
45 ctrl
|= MIIRD
| ((mii_id
<< 16) & PHYAD_MASK
) |
46 ((regnum
<< 21) & REGAD_MASK
);
48 writel(ctrl
, data
->base
+ REG_PHY_CTRL
);
51 ctrl
= readl(data
->base
+ REG_PHY_CTRL
);
54 return ctrl
& MIIRDATA_MASK
;
60 dev_dbg(&bus
->dev
, "%s timed out\n", __func__
);
65 static int moxart_mdio_write(struct mii_bus
*bus
, int mii_id
,
66 int regnum
, u16 value
)
68 struct moxart_mdio_data
*data
= bus
->priv
;
70 unsigned int count
= 5;
72 dev_dbg(&bus
->dev
, "%s\n", __func__
);
74 ctrl
|= MIIWR
| ((mii_id
<< 16) & PHYAD_MASK
) |
75 ((regnum
<< 21) & REGAD_MASK
);
77 value
&= MIIWDATA_MASK
;
79 writel(value
, data
->base
+ REG_PHY_WRITE_DATA
);
80 writel(ctrl
, data
->base
+ REG_PHY_CTRL
);
83 ctrl
= readl(data
->base
+ REG_PHY_CTRL
);
92 dev_dbg(&bus
->dev
, "%s timed out\n", __func__
);
97 static int moxart_mdio_reset(struct mii_bus
*bus
)
101 for (i
= 0; i
< PHY_MAX_ADDR
; i
++) {
102 data
= moxart_mdio_read(bus
, i
, MII_BMCR
);
107 if (moxart_mdio_write(bus
, i
, MII_BMCR
, data
) < 0)
114 static int moxart_mdio_probe(struct platform_device
*pdev
)
116 struct device_node
*np
= pdev
->dev
.of_node
;
118 struct moxart_mdio_data
*data
;
119 struct resource
*res
;
122 bus
= mdiobus_alloc_size(sizeof(*data
));
126 bus
->name
= "MOXA ART Ethernet MII";
127 bus
->read
= &moxart_mdio_read
;
128 bus
->write
= &moxart_mdio_write
;
129 bus
->reset
= &moxart_mdio_reset
;
130 snprintf(bus
->id
, MII_BUS_ID_SIZE
, "%s-%d-mii", pdev
->name
, pdev
->id
);
131 bus
->parent
= &pdev
->dev
;
133 /* Setting PHY_IGNORE_INTERRUPT here even if it has no effect,
134 * of_mdiobus_register() sets these PHY_POLL.
135 * Ideally, the interrupt from MAC controller could be used to
136 * detect link state changes, not polling, i.e. if there was
137 * a way phy_driver could set PHY_HAS_INTERRUPT but have that
138 * interrupt handled in ethernet drivercode.
140 for (i
= 0; i
< PHY_MAX_ADDR
; i
++)
141 bus
->irq
[i
] = PHY_IGNORE_INTERRUPT
;
144 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
145 data
->base
= devm_ioremap_resource(&pdev
->dev
, res
);
146 if (IS_ERR(data
->base
)) {
147 ret
= PTR_ERR(data
->base
);
148 goto err_out_free_mdiobus
;
151 ret
= of_mdiobus_register(bus
, np
);
153 goto err_out_free_mdiobus
;
155 platform_set_drvdata(pdev
, bus
);
159 err_out_free_mdiobus
:
164 static int moxart_mdio_remove(struct platform_device
*pdev
)
166 struct mii_bus
*bus
= platform_get_drvdata(pdev
);
168 mdiobus_unregister(bus
);
174 static const struct of_device_id moxart_mdio_dt_ids
[] = {
175 { .compatible
= "moxa,moxart-mdio" },
178 MODULE_DEVICE_TABLE(of
, moxart_mdio_dt_ids
);
180 static struct platform_driver moxart_mdio_driver
= {
181 .probe
= moxart_mdio_probe
,
182 .remove
= moxart_mdio_remove
,
184 .name
= "moxart-mdio",
185 .of_match_table
= moxart_mdio_dt_ids
,
189 module_platform_driver(moxart_mdio_driver
);
191 MODULE_DESCRIPTION("MOXA ART MDIO interface driver");
192 MODULE_AUTHOR("Jonas Jensen <jonas.jensen@gmail.com>");
193 MODULE_LICENSE("GPL");