1 /*
2 * Copyright (c) 2023 SLB
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT infineon_xmc4xxx_mdio
8
9 #include <errno.h>
10
11 #include <soc.h>
12 #include <zephyr/device.h>
13 #include <zephyr/drivers/mdio.h>
14 #include <zephyr/drivers/pinctrl.h>
15 #include <zephyr/init.h>
16 #include <zephyr/kernel.h>
17
18 #include <xmc_scu.h>
19 #include <xmc_eth_mac.h>
20
21 #include <zephyr/logging/log.h>
22 LOG_MODULE_REGISTER(mdio_xmc4xxx, CONFIG_MDIO_LOG_LEVEL);
23
24 #define MDIO_TRANSFER_TIMEOUT_US 250000
25
26 #define MAX_MDC_FREQUENCY 2500000u /* 400ns period */
27 #define MIN_MDC_FREQUENCY 1000000u /* 1us period */
28
29 struct mdio_xmc4xxx_clock_divider {
30 uint8_t divider;
31 uint8_t reg_val;
32 };
33
34 static const struct mdio_xmc4xxx_clock_divider mdio_clock_divider[] = {
35 {.divider = 8, .reg_val = 2}, {.divider = 13, .reg_val = 3},
36 {.divider = 21, .reg_val = 0}, {.divider = 31, .reg_val = 1},
37 {.divider = 51, .reg_val = 4}, {.divider = 62, .reg_val = 5},
38 };
39
40 struct mdio_xmc4xxx_dev_data {
41 struct k_mutex mutex;
42 uint32_t reg_value_gmii_address;
43 };
44
45 struct mdio_xmc4xxx_dev_config {
46 ETH_GLOBAL_TypeDef *const regs;
47 const struct pinctrl_dev_config *pcfg;
48 uint8_t mdi_port_ctrl;
49 };
50
mdio_xmc4xxx_transfer(const struct device * dev,uint8_t phy_addr,uint8_t reg_addr,uint8_t is_write,uint16_t data_write,uint16_t * data_read)51 static int mdio_xmc4xxx_transfer(const struct device *dev, uint8_t phy_addr, uint8_t reg_addr,
52 uint8_t is_write, uint16_t data_write, uint16_t *data_read)
53 {
54 const struct mdio_xmc4xxx_dev_config *const dev_cfg = dev->config;
55 ETH_GLOBAL_TypeDef *const regs = dev_cfg->regs;
56 struct mdio_xmc4xxx_dev_data *const dev_data = dev->data;
57 uint32_t reg;
58 int ret = 0;
59
60 k_mutex_lock(&dev_data->mutex, K_FOREVER);
61
62 if ((regs->GMII_ADDRESS & ETH_GMII_ADDRESS_MB_Msk) != 0) {
63 ret = -EBUSY;
64 goto finish;
65 }
66
67 reg = dev_data->reg_value_gmii_address;
68 if (is_write) {
69 reg |= ETH_GMII_ADDRESS_MW_Msk;
70 regs->GMII_DATA = data_write;
71 }
72
73 regs->GMII_ADDRESS = reg | ETH_GMII_ADDRESS_MB_Msk |
74 FIELD_PREP(ETH_GMII_ADDRESS_PA_Msk, phy_addr) |
75 FIELD_PREP(ETH_GMII_ADDRESS_MR_Msk, reg_addr);
76
77 if (!WAIT_FOR((regs->GMII_ADDRESS & ETH_GMII_ADDRESS_MB_Msk) == 0,
78 MDIO_TRANSFER_TIMEOUT_US, k_msleep(5))) {
79 LOG_WRN("mdio transfer timedout");
80 ret = -ETIMEDOUT;
81 goto finish;
82 }
83
84 if (!is_write && data_read != NULL) {
85 *data_read = regs->GMII_DATA;
86 }
87
88 finish:
89 k_mutex_unlock(&dev_data->mutex);
90
91 return ret;
92 }
93
mdio_xmc4xxx_read(const struct device * dev,uint8_t phy_addr,uint8_t reg_addr,uint16_t * data)94 static int mdio_xmc4xxx_read(const struct device *dev, uint8_t phy_addr, uint8_t reg_addr,
95 uint16_t *data)
96 {
97 return mdio_xmc4xxx_transfer(dev, phy_addr, reg_addr, 0, 0, data);
98 }
99
mdio_xmc4xxx_write(const struct device * dev,uint8_t phy_addr,uint8_t reg_addr,uint16_t data)100 static int mdio_xmc4xxx_write(const struct device *dev, uint8_t phy_addr,
101 uint8_t reg_addr, uint16_t data)
102 {
103 return mdio_xmc4xxx_transfer(dev, phy_addr, reg_addr, 1, data, NULL);
104 }
105
mdio_xmc4xxx_bus_enable(const struct device * dev)106 static void mdio_xmc4xxx_bus_enable(const struct device *dev)
107 {
108 ARG_UNUSED(dev);
109 /* this will enable the clock for ETH, which generates to MDIO clk */
110 XMC_ETH_MAC_Enable(NULL);
111 }
112
mdio_xmc4xxx_bus_disable(const struct device * dev)113 static void mdio_xmc4xxx_bus_disable(const struct device *dev)
114 {
115 ARG_UNUSED(dev);
116 XMC_ETH_MAC_Disable(NULL);
117 }
118
mdio_xmc4xxx_set_clock_divider(const struct device * dev)119 static int mdio_xmc4xxx_set_clock_divider(const struct device *dev)
120 {
121 struct mdio_xmc4xxx_dev_data *dev_data = dev->data;
122 uint32_t eth_mac_clk = XMC_SCU_CLOCK_GetEthernetClockFrequency();
123
124 for (int i = 0; i < ARRAY_SIZE(mdio_clock_divider); i++) {
125 uint8_t divider = mdio_clock_divider[i].divider;
126 uint8_t reg_val = mdio_clock_divider[i].reg_val;
127 uint32_t mdc_clk = eth_mac_clk / divider;
128
129 if (mdc_clk > MIN_MDC_FREQUENCY && mdc_clk < MAX_MDC_FREQUENCY) {
130 LOG_DBG("Using MDC clock divider %d", divider);
131 LOG_DBG("MDC clock %dHz", mdc_clk);
132 dev_data->reg_value_gmii_address =
133 FIELD_PREP(ETH_GMII_ADDRESS_CR_Msk, reg_val);
134 return 0;
135 }
136 }
137
138 return -EINVAL;
139 }
140
mdio_xmc4xxx_initialize(const struct device * dev)141 static int mdio_xmc4xxx_initialize(const struct device *dev)
142 {
143 const struct mdio_xmc4xxx_dev_config *dev_cfg = dev->config;
144 struct mdio_xmc4xxx_dev_data *dev_data = dev->data;
145 XMC_ETH_MAC_PORT_CTRL_t port_ctrl = {0};
146 int ret;
147
148 k_mutex_init(&dev_data->mutex);
149
150 ret = pinctrl_apply_state(dev_cfg->pcfg, PINCTRL_STATE_DEFAULT);
151 if (ret != 0) {
152 return ret;
153 }
154
155 ret = mdio_xmc4xxx_set_clock_divider(dev);
156 if (ret != 0) {
157 LOG_ERR("Error setting MDIO clock divider");
158 return -EINVAL;
159 }
160
161 port_ctrl.mdio = dev_cfg->mdi_port_ctrl;
162 ETH0_CON->CON = port_ctrl.raw;
163
164 return ret;
165 }
166
167 static DEVICE_API(mdio, mdio_xmc4xxx_driver_api) = {
168 .read = mdio_xmc4xxx_read,
169 .write = mdio_xmc4xxx_write,
170 .bus_enable = mdio_xmc4xxx_bus_enable,
171 .bus_disable = mdio_xmc4xxx_bus_disable,
172 };
173
174 PINCTRL_DT_INST_DEFINE(0);
175 static const struct mdio_xmc4xxx_dev_config mdio_xmc4xxx_dev_config_0 = {
176 .regs = (ETH_GLOBAL_TypeDef *)DT_REG_ADDR(DT_INST_PARENT(0)),
177 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(0),
178 .mdi_port_ctrl = DT_INST_ENUM_IDX(0, mdi_port_ctrl),
179 };
180
181 static struct mdio_xmc4xxx_dev_data mdio_xmc4xxx_dev_data_0;
182
183 DEVICE_DT_INST_DEFINE(0, &mdio_xmc4xxx_initialize, NULL, &mdio_xmc4xxx_dev_data_0,
184 &mdio_xmc4xxx_dev_config_0, POST_KERNEL,
185 CONFIG_MDIO_INIT_PRIORITY, &mdio_xmc4xxx_driver_api);
186