1 /*
2 * Copyright (c) 2023 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT snps_dwcxgmac_mdio
8
9 #include <errno.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/mdio.h>
13 #include <zephyr/init.h>
14 #include <zephyr/logging/log.h>
15 #include <zephyr/drivers/reset.h>
16
17 LOG_MODULE_REGISTER(snps_dwcxgmac_mdio, CONFIG_MDIO_LOG_LEVEL);
18
19 #define XGMAC_DMA_BASE_ADDR_OFFSET (0x3000u)
20 #define DMA_MODE_OFST (0x0)
21 #define DMA_MODE_SWR_SET_MSK (0x00000001)
22 #define DMA_MODE_SWR_SET(value) ((value) & 0x00000001)
23
24 #define MDIO_READ_CMD (3u)
25 #define MDIO_WRITE_CMD (1u)
26
27 #define CORE_MDIO_SINGLE_COMMAND_ADDRESS_OFST 0x200
28 #define CORE_MDIO_SINGLE_COMMAND_ADDRESS_RA_SET(value) (((value) << 0) & 0x0000ffff)
29 #define CORE_MDIO_SINGLE_COMMAND_ADDRESS_PA_SET(value) (((value) << 16) & 0x001f0000)
30 #define CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_OFST 0x204
31 #define CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_SBUSY_SET_MSK BIT(22)
32 #define CORE_MDIO_CLAUSE_22_PORT_OFST 0x220
33 #define CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_SDATA_SET(value) (((value) << 0) & 0x0000ffff)
34 #define CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_CMD_SET(value) (((value) << 16) & 0x00030000)
35 #define CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_SADDR_SET(value) (((value) << 18) & 0x00040000)
36 #define CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_CR_SET(value) (((value) << 19) & 0x00380000)
37 #define CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_CRS_SET(value) (((value) << 31) & 0x80000000)
38 #define CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_SBUSY_SET(value) (((value) << 22) & 0x00400000)
39 #define CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_SDATA_GET(value) (((value) & 0x0000ffff) >> 0)
40
41 struct mdio_dwcxgmac_dev_data {
42 DEVICE_MMIO_RAM;
43 struct k_mutex mdio_transfer_lock;
44 };
45
46 struct mdio_dwcxgmac_dev_config {
47 DEVICE_MMIO_ROM;
48 uint32_t clk_range;
49 bool clk_range_sel;
50 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(resets)
51 /* XGMAC peripheral reset signal */
52 const struct reset_dt_spec reset;
53 #endif
54 };
55
dwxgmac_software_reset(mem_addr_t ioaddr)56 static inline int dwxgmac_software_reset(mem_addr_t ioaddr)
57 {
58 uint32_t delay_us = CONFIG_MDIO_DWCXGMAC_STATUS_BUSY_CHECK_TIMEOUT;
59 bool ret;
60 /* software reset */
61 mem_addr_t reg_addr = (mem_addr_t)(ioaddr + XGMAC_DMA_BASE_ADDR_OFFSET + DMA_MODE_OFST);
62
63 sys_write32(DMA_MODE_SWR_SET(1u), reg_addr);
64
65 ret = WAIT_FOR(!(sys_read32(reg_addr) & DMA_MODE_SWR_SET_MSK), delay_us, k_msleep(1));
66 if (ret == false) {
67 return -ETIMEDOUT;
68 }
69
70 return 0;
71 }
72
mdio_busy_wait(uint32_t reg_addr,uint32_t bit_msk)73 static inline int mdio_busy_wait(uint32_t reg_addr, uint32_t bit_msk)
74 {
75 uint32_t delay_us = CONFIG_MDIO_DWCXGMAC_STATUS_BUSY_CHECK_TIMEOUT;
76 bool ret;
77
78 ret = WAIT_FOR(!(sys_read32(reg_addr) & bit_msk), delay_us, k_msleep(1));
79 if (ret == false) {
80 return -ETIMEDOUT;
81 }
82
83 return 0;
84 }
85
mdio_transfer(const struct device * dev,uint8_t prtad,uint8_t devad,uint8_t rw,uint16_t data_in,uint16_t * data_out)86 static int mdio_transfer(const struct device *dev, uint8_t prtad, uint8_t devad, uint8_t rw,
87 uint16_t data_in, uint16_t *data_out)
88 {
89 const struct mdio_dwcxgmac_dev_config *const cfg =
90 (struct mdio_dwcxgmac_dev_config *)dev->config;
91 struct mdio_dwcxgmac_dev_data *const data = (struct mdio_dwcxgmac_dev_data *)dev->data;
92 int retval;
93 mem_addr_t ioaddr = 0;
94 uint32_t reg_addr;
95 uint32_t reg_data, mdio_addr, mdio_data = 0;
96
97 ioaddr = (mem_addr_t)DEVICE_MMIO_GET(dev);
98 retval = mdio_busy_wait((ioaddr + CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_OFST),
99 CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_SBUSY_SET_MSK);
100 if (retval) {
101 LOG_ERR("%s: MDIO device busy wait timedout", dev->name);
102 return retval;
103 }
104
105 (void)k_mutex_lock(&data->mdio_transfer_lock, K_FOREVER);
106 /* Set port as Clause 22 */
107 reg_addr = ioaddr + CORE_MDIO_CLAUSE_22_PORT_OFST;
108 reg_data = sys_read32(reg_addr);
109 reg_data |= BIT(prtad);
110 sys_write32(reg_data, reg_addr);
111
112 reg_addr = ioaddr + CORE_MDIO_SINGLE_COMMAND_ADDRESS_OFST;
113 mdio_addr = CORE_MDIO_SINGLE_COMMAND_ADDRESS_RA_SET(devad) |
114 CORE_MDIO_SINGLE_COMMAND_ADDRESS_PA_SET(prtad);
115 sys_write32(mdio_addr, reg_addr);
116
117 reg_addr = ioaddr + CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_OFST;
118 mdio_data = CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_SDATA_SET(data_in) |
119 CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_CMD_SET(rw) |
120 CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_SADDR_SET(1u) |
121 CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_CR_SET(cfg->clk_range) |
122 CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_CRS_SET(cfg->clk_range_sel) |
123 CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_SBUSY_SET(1u);
124
125 sys_write32(mdio_data, reg_addr);
126
127 retval = mdio_busy_wait(reg_addr, CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_SBUSY_SET_MSK);
128
129 if (retval) {
130 LOG_ERR("%s: transfer timedout", dev->name);
131 } else {
132 if (data_out) {
133 *data_out = CORE_MDIO_SINGLE_COMMAND_CONTROL_DATA_SDATA_GET(
134 sys_read32(reg_addr));
135 }
136 }
137
138 (void)k_mutex_unlock(&data->mdio_transfer_lock);
139
140 return retval;
141 }
142
mdio_dwcxgmac_read(const struct device * dev,uint8_t prtad,uint8_t regad,uint16_t * data)143 static int mdio_dwcxgmac_read(const struct device *dev, uint8_t prtad, uint8_t regad,
144 uint16_t *data)
145 {
146 return mdio_transfer(dev, prtad, regad, MDIO_READ_CMD, 0, data);
147 }
148
mdio_dwcxgmac_write(const struct device * dev,uint8_t prtad,uint8_t regad,uint16_t data)149 static int mdio_dwcxgmac_write(const struct device *dev, uint8_t prtad, uint8_t regad,
150 uint16_t data)
151 {
152 return mdio_transfer(dev, prtad, regad, MDIO_WRITE_CMD, data, NULL);
153 }
154
mdio_dwcxgmac_bus_enable(const struct device * dev)155 static void mdio_dwcxgmac_bus_enable(const struct device *dev)
156 {
157 ARG_UNUSED(dev);
158 }
159
mdio_dwcxgmac_bus_disable(const struct device * dev)160 static void mdio_dwcxgmac_bus_disable(const struct device *dev)
161 {
162 ARG_UNUSED(dev);
163 }
164
mdio_dwcxgmac_initialize(const struct device * dev)165 static int mdio_dwcxgmac_initialize(const struct device *dev)
166 {
167 struct mdio_dwcxgmac_dev_data *const data = (struct mdio_dwcxgmac_dev_data *)dev->data;
168 const struct mdio_dwcxgmac_dev_config *const cfg =
169 (struct mdio_dwcxgmac_dev_config *)dev->config;
170 mem_addr_t ioaddr;
171 int ret = 0;
172
173 #if DT_ANY_INST_HAS_PROP_STATUS_OKAY(resets)
174
175 if (cfg->reset.dev != NULL) {
176 if (!device_is_ready(cfg->reset.dev)) {
177 LOG_ERR("%s, Reset device is not ready", dev->name);
178 return -ENODEV;
179 }
180 ret = reset_line_toggle(cfg->reset.dev, cfg->reset.id);
181 if (ret) {
182 LOG_ERR("%s: Failed to reset peripheral", dev->name);
183 return ret;
184 }
185 } else {
186 LOG_ERR("%s, Reset device is not available", dev->name);
187 return -ENODEV;
188 }
189
190 #endif
191
192 DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
193 ioaddr = (mem_addr_t)DEVICE_MMIO_GET(dev);
194
195 ret = dwxgmac_software_reset(ioaddr);
196 if (ret) {
197 LOG_ERR("%s: XGMAC reset timeout", dev->name);
198 return ret;
199 }
200
201 k_mutex_init(&data->mdio_transfer_lock);
202
203 return 0;
204 }
205
206 static DEVICE_API(mdio, mdio_dwcxgmac_driver_api) = {
207 .read = mdio_dwcxgmac_read,
208 .write = mdio_dwcxgmac_write,
209 .bus_enable = mdio_dwcxgmac_bus_enable,
210 .bus_disable = mdio_dwcxgmac_bus_disable,
211 };
212
213 #define XGMAC_SNPS_DESIGNWARE_RESET_SPEC_INIT(n) .reset = RESET_DT_SPEC_INST_GET(n),
214
215 #define MDIO_DWCXGMAC_CONFIG(n) \
216 static const struct mdio_dwcxgmac_dev_config mdio_dwcxgmac_dev_config_##n = { \
217 DEVICE_MMIO_ROM_INIT(DT_DRV_INST(n)), \
218 .clk_range = DT_INST_PROP(n, csr_clock_indx), \
219 .clk_range_sel = DT_INST_PROP(n, clock_range_sel), \
220 IF_ENABLED(DT_INST_NODE_HAS_PROP(n, resets), \
221 (XGMAC_SNPS_DESIGNWARE_RESET_SPEC_INIT(n)))};
222
223 #define MDIO_DWCXGMAC_DEVICE(n) \
224 MDIO_DWCXGMAC_CONFIG(n); \
225 static struct mdio_dwcxgmac_dev_data mdio_dwcxgmac_dev_data##n; \
226 DEVICE_DT_INST_DEFINE(n, &mdio_dwcxgmac_initialize, NULL, &mdio_dwcxgmac_dev_data##n, \
227 &mdio_dwcxgmac_dev_config_##n, POST_KERNEL, \
228 CONFIG_MDIO_INIT_PRIORITY, &mdio_dwcxgmac_driver_api);
229
230 DT_INST_FOREACH_STATUS_OKAY(MDIO_DWCXGMAC_DEVICE)
231