0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/clk.h>
0009 #include <linux/iopoll.h>
0010 #include <linux/kernel.h>
0011 #include <linux/module.h>
0012 #include <linux/of_address.h>
0013 #include <linux/of_mdio.h>
0014 #include <linux/platform_device.h>
0015
0016 #define MDIO_RWCTRL 0x00
0017 #define MDIO_RO_DATA 0x04
0018 #define MDIO_WRITE BIT(13)
0019 #define MDIO_RW_FINISH BIT(15)
0020 #define BIT_PHY_ADDR_OFFSET 8
0021 #define BIT_WR_DATA_OFFSET 16
0022
0023 struct hisi_femac_mdio_data {
0024 struct clk *clk;
0025 void __iomem *membase;
0026 };
0027
0028 static int hisi_femac_mdio_wait_ready(struct hisi_femac_mdio_data *data)
0029 {
0030 u32 val;
0031
0032 return readl_poll_timeout(data->membase + MDIO_RWCTRL,
0033 val, val & MDIO_RW_FINISH, 20, 10000);
0034 }
0035
0036 static int hisi_femac_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
0037 {
0038 struct hisi_femac_mdio_data *data = bus->priv;
0039 int ret;
0040
0041 ret = hisi_femac_mdio_wait_ready(data);
0042 if (ret)
0043 return ret;
0044
0045 writel((mii_id << BIT_PHY_ADDR_OFFSET) | regnum,
0046 data->membase + MDIO_RWCTRL);
0047
0048 ret = hisi_femac_mdio_wait_ready(data);
0049 if (ret)
0050 return ret;
0051
0052 return readl(data->membase + MDIO_RO_DATA) & 0xFFFF;
0053 }
0054
0055 static int hisi_femac_mdio_write(struct mii_bus *bus, int mii_id, int regnum,
0056 u16 value)
0057 {
0058 struct hisi_femac_mdio_data *data = bus->priv;
0059 int ret;
0060
0061 ret = hisi_femac_mdio_wait_ready(data);
0062 if (ret)
0063 return ret;
0064
0065 writel(MDIO_WRITE | (value << BIT_WR_DATA_OFFSET) |
0066 (mii_id << BIT_PHY_ADDR_OFFSET) | regnum,
0067 data->membase + MDIO_RWCTRL);
0068
0069 return hisi_femac_mdio_wait_ready(data);
0070 }
0071
0072 static int hisi_femac_mdio_probe(struct platform_device *pdev)
0073 {
0074 struct device_node *np = pdev->dev.of_node;
0075 struct mii_bus *bus;
0076 struct hisi_femac_mdio_data *data;
0077 int ret;
0078
0079 bus = mdiobus_alloc_size(sizeof(*data));
0080 if (!bus)
0081 return -ENOMEM;
0082
0083 bus->name = "hisi_femac_mii_bus";
0084 bus->read = &hisi_femac_mdio_read;
0085 bus->write = &hisi_femac_mdio_write;
0086 snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
0087 bus->parent = &pdev->dev;
0088
0089 data = bus->priv;
0090 data->membase = devm_platform_ioremap_resource(pdev, 0);
0091 if (IS_ERR(data->membase)) {
0092 ret = PTR_ERR(data->membase);
0093 goto err_out_free_mdiobus;
0094 }
0095
0096 data->clk = devm_clk_get(&pdev->dev, NULL);
0097 if (IS_ERR(data->clk)) {
0098 ret = PTR_ERR(data->clk);
0099 goto err_out_free_mdiobus;
0100 }
0101
0102 ret = clk_prepare_enable(data->clk);
0103 if (ret)
0104 goto err_out_free_mdiobus;
0105
0106 ret = of_mdiobus_register(bus, np);
0107 if (ret)
0108 goto err_out_disable_clk;
0109
0110 platform_set_drvdata(pdev, bus);
0111
0112 return 0;
0113
0114 err_out_disable_clk:
0115 clk_disable_unprepare(data->clk);
0116 err_out_free_mdiobus:
0117 mdiobus_free(bus);
0118 return ret;
0119 }
0120
0121 static int hisi_femac_mdio_remove(struct platform_device *pdev)
0122 {
0123 struct mii_bus *bus = platform_get_drvdata(pdev);
0124 struct hisi_femac_mdio_data *data = bus->priv;
0125
0126 mdiobus_unregister(bus);
0127 clk_disable_unprepare(data->clk);
0128 mdiobus_free(bus);
0129
0130 return 0;
0131 }
0132
0133 static const struct of_device_id hisi_femac_mdio_dt_ids[] = {
0134 { .compatible = "hisilicon,hisi-femac-mdio" },
0135 { }
0136 };
0137 MODULE_DEVICE_TABLE(of, hisi_femac_mdio_dt_ids);
0138
0139 static struct platform_driver hisi_femac_mdio_driver = {
0140 .probe = hisi_femac_mdio_probe,
0141 .remove = hisi_femac_mdio_remove,
0142 .driver = {
0143 .name = "hisi-femac-mdio",
0144 .of_match_table = hisi_femac_mdio_dt_ids,
0145 },
0146 };
0147
0148 module_platform_driver(hisi_femac_mdio_driver);
0149
0150 MODULE_DESCRIPTION("Hisilicon Fast Ethernet MAC MDIO interface driver");
0151 MODULE_AUTHOR("Dongpo Li <lidongpo@hisilicon.com>");
0152 MODULE_LICENSE("GPL");