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