1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /* Copyright (C) 2019 IBM Corp. */
4 #include <linux/bitfield.h>
5 #include <linux/delay.h>
6 #include <linux/iopoll.h>
7 #include <linux/mdio.h>
8 #include <linux/module.h>
10 #include <linux/of_mdio.h>
11 #include <linux/phy.h>
12 #include <linux/platform_device.h>
14 #define DRV_NAME "mdio-aspeed"
16 #define ASPEED_MDIO_CTRL 0x0
17 #define ASPEED_MDIO_CTRL_FIRE BIT(31)
18 #define ASPEED_MDIO_CTRL_ST BIT(28)
19 #define ASPEED_MDIO_CTRL_ST_C45 0
20 #define ASPEED_MDIO_CTRL_ST_C22 1
21 #define ASPEED_MDIO_CTRL_OP GENMASK(27, 26)
22 #define MDIO_C22_OP_WRITE 0b01
23 #define MDIO_C22_OP_READ 0b10
24 #define ASPEED_MDIO_CTRL_PHYAD GENMASK(25, 21)
25 #define ASPEED_MDIO_CTRL_REGAD GENMASK(20, 16)
26 #define ASPEED_MDIO_CTRL_MIIWDATA GENMASK(15, 0)
28 #define ASPEED_MDIO_DATA 0x4
29 #define ASPEED_MDIO_DATA_MDC_THRES GENMASK(31, 24)
30 #define ASPEED_MDIO_DATA_MDIO_EDGE BIT(23)
31 #define ASPEED_MDIO_DATA_MDIO_LATCH GENMASK(22, 20)
32 #define ASPEED_MDIO_DATA_IDLE BIT(16)
33 #define ASPEED_MDIO_DATA_MIIRDATA GENMASK(15, 0)
35 #define ASPEED_MDIO_INTERVAL_US 100
36 #define ASPEED_MDIO_TIMEOUT_US (ASPEED_MDIO_INTERVAL_US * 10)
42 static int aspeed_mdio_read(struct mii_bus
*bus
, int addr
, int regnum
)
44 struct aspeed_mdio
*ctx
= bus
->priv
;
49 dev_dbg(&bus
->dev
, "%s: addr: %d, regnum: %d\n", __func__
, addr
,
52 /* Just clause 22 for the moment */
53 if (regnum
& MII_ADDR_C45
)
56 ctrl
= ASPEED_MDIO_CTRL_FIRE
57 | FIELD_PREP(ASPEED_MDIO_CTRL_ST
, ASPEED_MDIO_CTRL_ST_C22
)
58 | FIELD_PREP(ASPEED_MDIO_CTRL_OP
, MDIO_C22_OP_READ
)
59 | FIELD_PREP(ASPEED_MDIO_CTRL_PHYAD
, addr
)
60 | FIELD_PREP(ASPEED_MDIO_CTRL_REGAD
, regnum
);
62 iowrite32(ctrl
, ctx
->base
+ ASPEED_MDIO_CTRL
);
64 rc
= readl_poll_timeout(ctx
->base
+ ASPEED_MDIO_DATA
, data
,
65 data
& ASPEED_MDIO_DATA_IDLE
,
66 ASPEED_MDIO_INTERVAL_US
,
67 ASPEED_MDIO_TIMEOUT_US
);
71 return FIELD_GET(ASPEED_MDIO_DATA_MIIRDATA
, data
);
74 static int aspeed_mdio_write(struct mii_bus
*bus
, int addr
, int regnum
, u16 val
)
76 struct aspeed_mdio
*ctx
= bus
->priv
;
79 dev_dbg(&bus
->dev
, "%s: addr: %d, regnum: %d, val: 0x%x\n",
80 __func__
, addr
, regnum
, val
);
82 /* Just clause 22 for the moment */
83 if (regnum
& MII_ADDR_C45
)
86 ctrl
= ASPEED_MDIO_CTRL_FIRE
87 | FIELD_PREP(ASPEED_MDIO_CTRL_ST
, ASPEED_MDIO_CTRL_ST_C22
)
88 | FIELD_PREP(ASPEED_MDIO_CTRL_OP
, MDIO_C22_OP_WRITE
)
89 | FIELD_PREP(ASPEED_MDIO_CTRL_PHYAD
, addr
)
90 | FIELD_PREP(ASPEED_MDIO_CTRL_REGAD
, regnum
)
91 | FIELD_PREP(ASPEED_MDIO_CTRL_MIIWDATA
, val
);
93 iowrite32(ctrl
, ctx
->base
+ ASPEED_MDIO_CTRL
);
95 return readl_poll_timeout(ctx
->base
+ ASPEED_MDIO_CTRL
, ctrl
,
96 !(ctrl
& ASPEED_MDIO_CTRL_FIRE
),
97 ASPEED_MDIO_INTERVAL_US
,
98 ASPEED_MDIO_TIMEOUT_US
);
101 static int aspeed_mdio_probe(struct platform_device
*pdev
)
103 struct aspeed_mdio
*ctx
;
107 bus
= devm_mdiobus_alloc_size(&pdev
->dev
, sizeof(*ctx
));
112 ctx
->base
= devm_platform_ioremap_resource(pdev
, 0);
113 if (IS_ERR(ctx
->base
))
114 return PTR_ERR(ctx
->base
);
116 bus
->name
= DRV_NAME
;
117 snprintf(bus
->id
, MII_BUS_ID_SIZE
, "%s%d", pdev
->name
, pdev
->id
);
118 bus
->parent
= &pdev
->dev
;
119 bus
->read
= aspeed_mdio_read
;
120 bus
->write
= aspeed_mdio_write
;
122 rc
= of_mdiobus_register(bus
, pdev
->dev
.of_node
);
124 dev_err(&pdev
->dev
, "Cannot register MDIO bus!\n");
128 platform_set_drvdata(pdev
, bus
);
133 static int aspeed_mdio_remove(struct platform_device
*pdev
)
135 mdiobus_unregister(platform_get_drvdata(pdev
));
140 static const struct of_device_id aspeed_mdio_of_match
[] = {
141 { .compatible
= "aspeed,ast2600-mdio", },
145 static struct platform_driver aspeed_mdio_driver
= {
148 .of_match_table
= aspeed_mdio_of_match
,
150 .probe
= aspeed_mdio_probe
,
151 .remove
= aspeed_mdio_remove
,
154 module_platform_driver(aspeed_mdio_driver
);
156 MODULE_AUTHOR("Andrew Jeffery <andrew@aj.id.au>");
157 MODULE_LICENSE("GPL");