Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 /*
0003  * Hisilicon Fast Ethernet MDIO Bus Driver
0004  *
0005  * Copyright (c) 2016 HiSilicon Technologies Co., Ltd.
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");