blob: 0b59d0609175fc7464185391acd28a0ebacd3ad0 [file] [log] [blame]
Yang Xiwendabe1db2023-08-23 01:03:43 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Hisilicon Fast Ethernet MDIO Bus Driver
4 *
5 * Copyright (c) 2016 HiSilicon Technologies Co., Ltd.
6 */
7
8#include <dm.h>
9#include <clk.h>
10#include <miiphy.h>
Yang Xiwen5090ead2024-01-22 22:33:20 +080011#include <dm/device_compat.h>
Yang Xiwendabe1db2023-08-23 01:03:43 +080012#include <linux/io.h>
13#include <linux/iopoll.h>
14
15#define MDIO_RWCTRL 0x00
16#define MDIO_RO_DATA 0x04
17#define MDIO_WRITE BIT(13)
18#define MDIO_RW_FINISH BIT(15)
19#define BIT_PHY_ADDR_OFFSET 8
20#define BIT_WR_DATA_OFFSET 16
21
22struct hisi_femac_mdio_data {
23 struct clk *clk;
24 void __iomem *membase;
25};
26
27static int hisi_femac_mdio_wait_ready(struct hisi_femac_mdio_data *data)
28{
29 u32 val;
30
31 return readl_poll_timeout(data->membase + MDIO_RWCTRL,
32 val, val & MDIO_RW_FINISH, 10000);
33}
34
35static int hisi_femac_mdio_read(struct udevice *dev, int addr, int devad, int reg)
36{
37 struct hisi_femac_mdio_data *data = dev_get_priv(dev);
38 int ret;
39
40 ret = hisi_femac_mdio_wait_ready(data);
41 if (ret)
42 return ret;
43
44 writel((addr << BIT_PHY_ADDR_OFFSET) | reg,
45 data->membase + MDIO_RWCTRL);
46
47 ret = hisi_femac_mdio_wait_ready(data);
48 if (ret)
49 return ret;
50
51 return readl(data->membase + MDIO_RO_DATA) & 0xFFFF;
52}
53
54static int hisi_femac_mdio_write(struct udevice *dev, int addr, int devad, int reg, u16 val)
55{
56 struct hisi_femac_mdio_data *data = dev_get_priv(dev);
57 int ret;
58
59 ret = hisi_femac_mdio_wait_ready(data);
60 if (ret)
61 return ret;
62
63 writel(MDIO_WRITE | (val << BIT_WR_DATA_OFFSET) |
64 (addr << BIT_PHY_ADDR_OFFSET) | reg,
65 data->membase + MDIO_RWCTRL);
66
67 return hisi_femac_mdio_wait_ready(data);
68}
69
70static int hisi_femac_mdio_of_to_plat(struct udevice *dev)
71{
72 struct hisi_femac_mdio_data *data = dev_get_priv(dev);
73 int ret;
74
75 data->membase = dev_remap_addr(dev);
76 if (IS_ERR(data->membase)) {
77 ret = PTR_ERR(data->membase);
Yang Xiwen5090ead2024-01-22 22:33:20 +080078 dev_err(dev, "Failed to remap base addr %d\n", ret);
79 return log_msg_ret("mdio", ret);
Yang Xiwendabe1db2023-08-23 01:03:43 +080080 }
81
82 // clk is optional
83 data->clk = devm_clk_get_optional(dev, NULL);
84
85 return 0;
86}
87
88static int hisi_femac_mdio_probe(struct udevice *dev)
89{
90 struct hisi_femac_mdio_data *data = dev_get_priv(dev);
91 int ret;
92
93 ret = clk_prepare_enable(data->clk);
Yang Xiwen5090ead2024-01-22 22:33:20 +080094 if (ret) {
95 dev_err(dev, "Failed to enable clock: %d\n", ret);
96 return log_msg_ret("clk", ret);
97 }
Yang Xiwendabe1db2023-08-23 01:03:43 +080098
99 return 0;
100}
101
102static const struct mdio_ops hisi_femac_mdio_ops = {
103 .read = hisi_femac_mdio_read,
104 .write = hisi_femac_mdio_write,
105};
106
107static const struct udevice_id hisi_femac_mdio_dt_ids[] = {
108 { .compatible = "hisilicon,hisi-femac-mdio" },
109 { }
110};
111
112U_BOOT_DRIVER(hisi_femac_mdio_driver) = {
113 .name = "hisi-femac-mdio",
114 .id = UCLASS_MDIO,
115 .of_match = hisi_femac_mdio_dt_ids,
116 .of_to_plat = hisi_femac_mdio_of_to_plat,
117 .probe = hisi_femac_mdio_probe,
118 .ops = &hisi_femac_mdio_ops,
Yang Xiwen5090ead2024-01-22 22:33:20 +0800119 .plat_auto = sizeof(struct mdio_perdev_priv),
Yang Xiwendabe1db2023-08-23 01:03:43 +0800120 .priv_auto = sizeof(struct hisi_femac_mdio_data),
121};