|
| 1 | +/* |
| 2 | + * Copyright (c) 2025 ENE Technology Inc. |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#define DT_DRV_COMPAT ene_kb106x_uart |
| 8 | + |
| 9 | +#include <zephyr/kernel.h> |
| 10 | +#include <zephyr/drivers/uart.h> |
| 11 | +#include <zephyr/drivers/pinctrl.h> |
| 12 | +#include <reg/ser.h> |
| 13 | + |
| 14 | +struct kb106x_uart_config { |
| 15 | +#ifdef CONFIG_UART_INTERRUPT_DRIVEN |
| 16 | + void (*irq_cfg_func)(void); |
| 17 | +#endif |
| 18 | + struct serial_regs *ser; |
| 19 | + const struct pinctrl_dev_config *pcfg; |
| 20 | +}; |
| 21 | + |
| 22 | +struct kb106x_uart_data { |
| 23 | + uart_irq_callback_user_data_t callback; |
| 24 | + struct uart_config current_config; |
| 25 | + void *callback_data; |
| 26 | + uint8_t pending_flag_data; |
| 27 | +}; |
| 28 | + |
| 29 | +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE |
| 30 | +static int kb106x_uart_configure(const struct device *dev, const struct uart_config *cfg) |
| 31 | +{ |
| 32 | + uint16_t reg_baudrate = 0; |
| 33 | + int ret = 0; |
| 34 | + const struct kb106x_uart_config *config = dev->config; |
| 35 | + struct kb106x_uart_data *data = dev->data; |
| 36 | + |
| 37 | + reg_baudrate = (DIVIDER_BASE_CLK / cfg->baudrate) - 1; |
| 38 | + |
| 39 | + switch (cfg->parity) { |
| 40 | + case UART_CFG_PARITY_NONE: |
| 41 | + break; |
| 42 | + case UART_CFG_PARITY_ODD: |
| 43 | + case UART_CFG_PARITY_EVEN: |
| 44 | + case UART_CFG_PARITY_MARK: |
| 45 | + case UART_CFG_PARITY_SPACE: |
| 46 | + default: |
| 47 | + ret = -ENOTSUP; |
| 48 | + break; |
| 49 | + } |
| 50 | + |
| 51 | + switch (cfg->stop_bits) { |
| 52 | + case UART_CFG_STOP_BITS_1: |
| 53 | + break; |
| 54 | + case UART_CFG_STOP_BITS_0_5: |
| 55 | + case UART_CFG_STOP_BITS_1_5: |
| 56 | + case UART_CFG_STOP_BITS_2: |
| 57 | + default: |
| 58 | + ret = -ENOTSUP; |
| 59 | + break; |
| 60 | + } |
| 61 | + |
| 62 | + switch (cfg->data_bits) { |
| 63 | + case UART_CFG_DATA_BITS_8: |
| 64 | + break; |
| 65 | + case UART_CFG_DATA_BITS_5: |
| 66 | + case UART_CFG_DATA_BITS_6: |
| 67 | + case UART_CFG_DATA_BITS_7: |
| 68 | + case UART_CFG_DATA_BITS_9: |
| 69 | + default: |
| 70 | + ret = -ENOTSUP; |
| 71 | + break; |
| 72 | + } |
| 73 | + |
| 74 | + switch (cfg->flow_ctrl) { |
| 75 | + case UART_CFG_FLOW_CTRL_NONE: |
| 76 | + break; |
| 77 | + case UART_CFG_FLOW_CTRL_RTS_CTS: |
| 78 | + case UART_CFG_FLOW_CTRL_DTR_DSR: |
| 79 | + default: |
| 80 | + ret = -ENOTSUP; |
| 81 | + break; |
| 82 | + } |
| 83 | + config->ser->SERCFG = (reg_baudrate << 16) | 0x04 | SERIE_TX_ENABLE; |
| 84 | + config->ser->SERCTRL = SERCTRL_MODE1; |
| 85 | + data->current_config = *cfg; |
| 86 | + return ret; |
| 87 | +} |
| 88 | + |
| 89 | +static int kb106x_uart_config_get(const struct device *dev, struct uart_config *cfg) |
| 90 | +{ |
| 91 | + struct kb106x_uart_data *data = dev->data; |
| 92 | + |
| 93 | + *cfg = data->current_config; |
| 94 | + return 0; |
| 95 | +} |
| 96 | +#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ |
| 97 | + |
| 98 | +#ifdef CONFIG_UART_INTERRUPT_DRIVEN |
| 99 | +static int kb106x_uart_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size) |
| 100 | +{ |
| 101 | + const struct kb106x_uart_config *config = dev->config; |
| 102 | + uint16_t tx_bytes = 0U; |
| 103 | + |
| 104 | + while ((size - tx_bytes) > 0) { |
| 105 | + /* Check Tx FIFO not Full*/ |
| 106 | + while (config->ser->SERSTS & SERSTS_TX_FULL) { |
| 107 | + } |
| 108 | + /* Put a character into Tx FIFO */ |
| 109 | + config->ser->SERTBUF = tx_data[tx_bytes]; |
| 110 | + tx_bytes++; |
| 111 | + } |
| 112 | + return tx_bytes; |
| 113 | +} |
| 114 | + |
| 115 | +static void kb106x_uart_irq_tx_enable(const struct device *dev) |
| 116 | +{ |
| 117 | + const struct kb106x_uart_config *config = dev->config; |
| 118 | + |
| 119 | + config->ser->SERPF = SERPF_TX_EMPTY; |
| 120 | + config->ser->SERIE |= SERIE_TX_ENABLE; |
| 121 | +} |
| 122 | + |
| 123 | +static void kb106x_uart_irq_tx_disable(const struct device *dev) |
| 124 | +{ |
| 125 | + const struct kb106x_uart_config *config = dev->config; |
| 126 | + |
| 127 | + config->ser->SERIE &= ~SERIE_TX_ENABLE; |
| 128 | + config->ser->SERPF = SERPF_TX_EMPTY; |
| 129 | +} |
| 130 | + |
| 131 | +static int kb106x_uart_irq_tx_ready(const struct device *dev) |
| 132 | +{ |
| 133 | + struct kb106x_uart_data *data = dev->data; |
| 134 | + |
| 135 | + return (data->pending_flag_data & SERPF_TX_EMPTY) ? 1 : 0; |
| 136 | +} |
| 137 | + |
| 138 | +static int kb106x_uart_irq_is_pending(const struct device *dev) |
| 139 | +{ |
| 140 | + struct kb106x_uart_data *data = dev->data; |
| 141 | + |
| 142 | + return (data->pending_flag_data) ? 1 : 0; |
| 143 | +} |
| 144 | + |
| 145 | +static int kb106x_uart_irq_update(const struct device *dev) |
| 146 | +{ |
| 147 | + struct kb106x_uart_data *data = dev->data; |
| 148 | + const struct kb106x_uart_config *config = dev->config; |
| 149 | + |
| 150 | + data->pending_flag_data = (config->ser->SERPF) & (config->ser->SERIE); |
| 151 | + /*clear pending flag*/ |
| 152 | + config->ser->SERPF = data->pending_flag_data; |
| 153 | + return 1; |
| 154 | +} |
| 155 | + |
| 156 | +static void kb106x_uart_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb, |
| 157 | + void *cb_data) |
| 158 | +{ |
| 159 | + struct kb106x_uart_data *data = dev->data; |
| 160 | + |
| 161 | + data->callback = cb; |
| 162 | + data->callback_data = cb_data; |
| 163 | +} |
| 164 | + |
| 165 | +static void kb106x_uart_irq_handler(const struct device *dev) |
| 166 | +{ |
| 167 | + struct kb106x_uart_data *data = dev->data; |
| 168 | + |
| 169 | + if (data->callback) { |
| 170 | + data->callback(dev, data->callback_data); |
| 171 | + } |
| 172 | +} |
| 173 | +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ |
| 174 | + |
| 175 | +static int kb106x_uart_poll_in(const struct device *dev, unsigned char *c) |
| 176 | +{ |
| 177 | + const struct kb106x_uart_config *config = dev->config; |
| 178 | + |
| 179 | + /* Check Rx FIFO not Empty*/ |
| 180 | + if (config->ser->SERSTS & SERSTS_RX_BUSY) { |
| 181 | + return -1; |
| 182 | + } |
| 183 | + /* Put a character into Tx FIFO */ |
| 184 | + *c = config->ser->SERRBUF; |
| 185 | + return 0; |
| 186 | +} |
| 187 | + |
| 188 | +static void kb106x_uart_poll_out(const struct device *dev, unsigned char c) |
| 189 | +{ |
| 190 | +#ifdef CONFIG_UART_INTERRUPT_DRIVEN |
| 191 | + kb106x_uart_fifo_fill(dev, &c, 1); |
| 192 | +#else |
| 193 | + const struct kb106x_uart_config *config = dev->config; |
| 194 | + |
| 195 | + /* Wait Tx FIFO not Full*/ |
| 196 | + while (config->ser->SERSTS & SERSTS_TX_FULL) { |
| 197 | + } |
| 198 | + /* Put a character into Tx FIFO */ |
| 199 | + config->ser->SERTBUF = c; |
| 200 | +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ |
| 201 | +} |
| 202 | + |
| 203 | +static DEVICE_API(uart, kb106x_uart_api) = { |
| 204 | + .poll_in = kb106x_uart_poll_in, |
| 205 | + .poll_out = kb106x_uart_poll_out, |
| 206 | +#ifdef CONFIG_UART_USE_RUNTIME_CONFIGURE |
| 207 | + .configure = kb106x_uart_configure, |
| 208 | + .config_get = kb106x_uart_config_get, |
| 209 | +#endif /* CONFIG_UART_USE_RUNTIME_CONFIGURE */ |
| 210 | +#ifdef CONFIG_UART_INTERRUPT_DRIVEN |
| 211 | + .fifo_fill = kb106x_uart_fifo_fill, |
| 212 | + .irq_tx_enable = kb106x_uart_irq_tx_enable, |
| 213 | + .irq_tx_disable = kb106x_uart_irq_tx_disable, |
| 214 | + .irq_tx_ready = kb106x_uart_irq_tx_ready, |
| 215 | + .irq_is_pending = kb106x_uart_irq_is_pending, |
| 216 | + .irq_update = kb106x_uart_irq_update, |
| 217 | + .irq_callback_set = kb106x_uart_irq_callback_set, |
| 218 | +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ |
| 219 | +}; |
| 220 | + |
| 221 | +#ifdef CONFIG_UART_INTERRUPT_DRIVEN |
| 222 | + |
| 223 | +/* GPIO module instances */ |
| 224 | +#define KB106X_UART_DEV(inst) DEVICE_DT_INST_GET(inst), |
| 225 | +static const struct device *const uart_devices[] = {DT_INST_FOREACH_STATUS_OKAY(KB106X_UART_DEV)}; |
| 226 | +static void kb106x_uart_isr_wrap(const struct device *dev) |
| 227 | +{ |
| 228 | + for (size_t i = 0; i < ARRAY_SIZE(uart_devices); i++) { |
| 229 | + const struct device *dev_ = uart_devices[i]; |
| 230 | + const struct kb106x_uart_config *config = dev_->config; |
| 231 | + |
| 232 | + if (config->ser->SERIE & config->ser->SERPF) { |
| 233 | + kb106x_uart_irq_handler(dev_); |
| 234 | + } |
| 235 | + } |
| 236 | +} |
| 237 | +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ |
| 238 | + |
| 239 | +static int kb106x_uart_init(const struct device *dev) |
| 240 | +{ |
| 241 | + int ret; |
| 242 | + const struct kb106x_uart_config *config = dev->config; |
| 243 | + struct kb106x_uart_data *data = dev->data; |
| 244 | + |
| 245 | + ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); |
| 246 | + if (ret != 0) { |
| 247 | + return ret; |
| 248 | + } |
| 249 | + |
| 250 | + kb106x_uart_configure(dev, &data->current_config); |
| 251 | +#ifdef CONFIG_UART_INTERRUPT_DRIVEN |
| 252 | + config->irq_cfg_func(); |
| 253 | +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ |
| 254 | + |
| 255 | + return 0; |
| 256 | +} |
| 257 | + |
| 258 | +#ifdef CONFIG_UART_INTERRUPT_DRIVEN |
| 259 | +static bool init_irq = true; |
| 260 | +static void kb106x_uart_irq_init(void) |
| 261 | +{ |
| 262 | + if (init_irq) { |
| 263 | + init_irq = false; |
| 264 | + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), kb106x_uart_isr_wrap, NULL, |
| 265 | + 0); |
| 266 | + irq_enable(DT_INST_IRQN(0)); |
| 267 | + } |
| 268 | +} |
| 269 | +#endif /* CONFIG_UART_INTERRUPT_DRIVEN */ |
| 270 | + |
| 271 | +#define KB106X_UART_INIT(n) \ |
| 272 | + PINCTRL_DT_INST_DEFINE(n); \ |
| 273 | + static struct kb106x_uart_data kb106x_uart_data_##n = { \ |
| 274 | + .current_config = { \ |
| 275 | + .baudrate = DT_INST_PROP(n, current_speed), \ |
| 276 | + .parity = UART_CFG_PARITY_NONE, \ |
| 277 | + .stop_bits = UART_CFG_STOP_BITS_1, \ |
| 278 | + .data_bits = UART_CFG_DATA_BITS_8, \ |
| 279 | + .flow_ctrl = UART_CFG_FLOW_CTRL_NONE, \ |
| 280 | + }, \ |
| 281 | + }; \ |
| 282 | + static const struct kb106x_uart_config kb106x_uart_config_##n = { \ |
| 283 | + IF_ENABLED(CONFIG_UART_INTERRUPT_DRIVEN, (.irq_cfg_func = kb106x_uart_irq_init,)) \ |
| 284 | + .ser = (struct serial_regs *)DT_INST_REG_ADDR(n), \ |
| 285 | + .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ |
| 286 | + }; \ |
| 287 | + DEVICE_DT_INST_DEFINE(n, &kb106x_uart_init, NULL, &kb106x_uart_data_##n, \ |
| 288 | + &kb106x_uart_config_##n, PRE_KERNEL_1, CONFIG_SERIAL_INIT_PRIORITY, \ |
| 289 | + &kb106x_uart_api); |
| 290 | + |
| 291 | +DT_INST_FOREACH_STATUS_OKAY(KB106X_UART_INIT) |
0 commit comments