1 /*
2 * Copyright (c) 2024 Vogl Electronic GmbH
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT litex_liteeth_mdio
8
9 #include <zephyr/device.h>
10 #include <zephyr/init.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/drivers/mdio.h>
13
14 #include <soc.h>
15
16 #include <zephyr/logging/log.h>
17 LOG_MODULE_REGISTER(litex_liteeth_mdio, CONFIG_MDIO_LOG_LEVEL);
18
19 #define LITEX_MDIO_CLK BIT(0)
20 #define LITEX_MDIO_OE BIT(1)
21 #define LITEX_MDIO_DO BIT(2)
22
23 #define LITEX_MDIO_DI BIT(0)
24
25 #define LITEX_MDIO_READ_OP 0
26 #define LITEX_MDIO_WRITE_OP 1
27 #define LITEX_MDIO_MSB 0x80000000
28
29 struct mdio_litex_data {
30 struct k_sem sem;
31 };
32
33 struct mdio_litex_config {
34 uint32_t w_addr;
35 uint32_t r_addr;
36 };
37
mdio_litex_read(const struct mdio_litex_config * dev_cfg,uint16_t * pdata)38 static void mdio_litex_read(const struct mdio_litex_config *dev_cfg, uint16_t *pdata)
39 {
40 uint16_t data = 0;
41
42 for (int i = 0; i < 16; i++) {
43 data <<= 1;
44 if (litex_read8(dev_cfg->r_addr) & LITEX_MDIO_DI) {
45 data |= 1;
46 }
47 litex_write8(LITEX_MDIO_CLK, dev_cfg->w_addr);
48 k_busy_wait(1);
49 litex_write8(0, dev_cfg->w_addr);
50 k_busy_wait(1);
51 }
52
53 LOG_DBG("Read data: 0x%04x", data);
54
55 *pdata = data;
56 }
57
mdio_litex_write(const struct mdio_litex_config * dev_cfg,uint32_t data,uint8_t len)58 static void mdio_litex_write(const struct mdio_litex_config *dev_cfg, uint32_t data, uint8_t len)
59 {
60 uint32_t v_data = data;
61 uint32_t v_len = len;
62
63 LOG_DBG("Write data: 0x%08x", data);
64
65 v_data <<= 32 - v_len;
66 while (v_len > 0) {
67 if (v_data & LITEX_MDIO_MSB) {
68 litex_write8(LITEX_MDIO_DO | LITEX_MDIO_OE, dev_cfg->w_addr);
69 k_busy_wait(1);
70 litex_write8(LITEX_MDIO_CLK | LITEX_MDIO_DO | LITEX_MDIO_OE,
71 dev_cfg->w_addr);
72 k_busy_wait(1);
73 litex_write8(LITEX_MDIO_DO | LITEX_MDIO_OE, dev_cfg->w_addr);
74 } else {
75 litex_write8(LITEX_MDIO_OE, dev_cfg->w_addr);
76 k_busy_wait(1);
77 litex_write8(LITEX_MDIO_CLK | LITEX_MDIO_OE, dev_cfg->w_addr);
78 k_busy_wait(1);
79 litex_write8(LITEX_MDIO_OE, dev_cfg->w_addr);
80 }
81 v_data <<= 1;
82 v_len--;
83 }
84 }
85
mdio_litex_turnaround(const struct mdio_litex_config * dev_cfg)86 static void mdio_litex_turnaround(const struct mdio_litex_config *dev_cfg)
87 {
88 k_busy_wait(1);
89 litex_write8(LITEX_MDIO_CLK, dev_cfg->w_addr);
90 k_busy_wait(1);
91 litex_write8(0, dev_cfg->w_addr);
92 k_busy_wait(1);
93 litex_write8(LITEX_MDIO_CLK, dev_cfg->w_addr);
94 k_busy_wait(1);
95 litex_write8(0, dev_cfg->w_addr);
96 }
97
mdio_litex_transfer(const struct device * dev,uint8_t prtad,uint8_t devad,uint8_t rw,uint16_t data_in,uint16_t * data_out)98 static int mdio_litex_transfer(const struct device *dev, uint8_t prtad, uint8_t devad, uint8_t rw,
99 uint16_t data_in, uint16_t *data_out)
100 {
101 const struct mdio_litex_config *const dev_cfg = dev->config;
102 struct mdio_litex_data *const dev_data = dev->data;
103
104 k_sem_take(&dev_data->sem, K_FOREVER);
105
106 litex_write8(LITEX_MDIO_OE, dev_cfg->w_addr);
107 /* PRE32: 32 bits '1' for sync*/
108 mdio_litex_write(dev_cfg, 0xFFFFFFFF, 32);
109 /* ST: 2 bits start of frame */
110 mdio_litex_write(dev_cfg, 0x1, 2);
111 /* OP: 2 bits opcode, read '10' or write '01' */
112 mdio_litex_write(dev_cfg, rw ? 0x1 : 0x2, 2);
113 /* PA5: 5 bits PHY address */
114 mdio_litex_write(dev_cfg, prtad, 5);
115 /* RA5: 5 bits register address */
116 mdio_litex_write(dev_cfg, devad, 5);
117
118 if (rw) { /* Write data */
119 /* TA: 2 bits turn-around */
120 mdio_litex_write(dev_cfg, 0x2, 2);
121 mdio_litex_write(dev_cfg, data_in, 16);
122 } else { /* Read data */
123 mdio_litex_turnaround(dev_cfg);
124 mdio_litex_read(dev_cfg, data_out);
125 }
126
127 mdio_litex_turnaround(dev_cfg);
128
129 k_sem_give(&dev_data->sem);
130
131 return 0;
132 }
133
mdio_litex_read_mmi(const struct device * dev,uint8_t prtad,uint8_t devad,uint16_t * data)134 static int mdio_litex_read_mmi(const struct device *dev, uint8_t prtad, uint8_t devad,
135 uint16_t *data)
136 {
137 return mdio_litex_transfer(dev, prtad, devad, LITEX_MDIO_READ_OP, 0, data);
138 }
139
mdio_litex_write_mmi(const struct device * dev,uint8_t prtad,uint8_t devad,uint16_t data)140 static int mdio_litex_write_mmi(const struct device *dev, uint8_t prtad, uint8_t devad,
141 uint16_t data)
142 {
143 return mdio_litex_transfer(dev, prtad, devad, LITEX_MDIO_WRITE_OP, data, NULL);
144 }
145
mdio_litex_initialize(const struct device * dev)146 static int mdio_litex_initialize(const struct device *dev)
147 {
148 struct mdio_litex_data *const dev_data = dev->data;
149
150 k_sem_init(&dev_data->sem, 1, 1);
151
152 return 0;
153 }
154
155 static DEVICE_API(mdio, mdio_litex_driver_api) = {
156 .read = mdio_litex_read_mmi,
157 .write = mdio_litex_write_mmi,
158 };
159
160 #define MDIO_LITEX_DEVICE(inst) \
161 static struct mdio_litex_config mdio_litex_dev_config_##inst = { \
162 .w_addr = DT_INST_REG_ADDR_BY_NAME(inst, mdio_w), \
163 .r_addr = DT_INST_REG_ADDR_BY_NAME(inst, mdio_r), \
164 }; \
165 static struct mdio_litex_data mdio_litex_dev_data_##inst; \
166 DEVICE_DT_INST_DEFINE(inst, &mdio_litex_initialize, NULL, &mdio_litex_dev_data_##inst, \
167 &mdio_litex_dev_config_##inst, POST_KERNEL, \
168 CONFIG_MDIO_INIT_PRIORITY, &mdio_litex_driver_api);
169
170 DT_INST_FOREACH_STATUS_OKAY(MDIO_LITEX_DEVICE)
171