Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | /* * Copyright (c) 2017 Google LLC. * * SPDX-License-Identifier: Apache-2.0 */ #include <device.h> #include <errno.h> #include <init.h> #include <misc/__assert.h> #include <soc.h> #include <uart.h> #include <board.h> /* Device constant configuration parameters */ struct uart_sam0_dev_cfg { SercomUsart *regs; u32_t baudrate; u32_t pads; u32_t pm_apbcmask; u16_t gclk_clkctrl_id; #if CONFIG_UART_INTERRUPT_DRIVEN void (*irq_config_func)(struct device *dev); #endif }; /* Device run time data */ struct uart_sam0_dev_data { #ifdef CONFIG_UART_INTERRUPT_DRIVEN uart_irq_callback_t cb; #endif }; #define DEV_CFG(dev) \ ((const struct uart_sam0_dev_cfg *const)(dev)->config->config_info) #define DEV_DATA(dev) ((struct uart_sam0_dev_data * const)(dev)->driver_data) static void wait_synchronization(SercomUsart *const usart) { #if defined(SERCOM_USART_SYNCBUSY_MASK) /* SYNCBUSY is a register */ while ((usart->SYNCBUSY.reg & SERCOM_USART_SYNCBUSY_MASK) != 0) { } #elif defined(SERCOM_USART_STATUS_SYNCBUSY) /* SYNCBUSY is a bit */ while ((usart->STATUS.reg & SERCOM_USART_STATUS_SYNCBUSY) != 0) { } #else #error Unsupported device #endif } static int uart_sam0_set_baudrate(SercomUsart *const usart, u32_t baudrate, u32_t clk_freq_hz) { u64_t tmp; u16_t baud; tmp = (u64_t)baudrate << 20; tmp = (tmp + (clk_freq_hz >> 1)) / clk_freq_hz; /* Verify that the calculated result is within range */ if (tmp < 1 || tmp > UINT16_MAX) { return -ERANGE; } baud = 65536 - (u16_t)tmp; usart->BAUD.reg = baud; wait_synchronization(usart); return 0; } static int uart_sam0_init(struct device *dev) { int retval; const struct uart_sam0_dev_cfg *const cfg = DEV_CFG(dev); SercomUsart *const usart = cfg->regs; /* Enable the GCLK */ GCLK->CLKCTRL.reg = cfg->gclk_clkctrl_id | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_CLKEN; /* Enable SERCOM clock in PM */ PM->APBCMASK.reg |= cfg->pm_apbcmask; /* Disable all USART interrupts */ usart->INTENCLR.reg = SERCOM_USART_INTENCLR_MASK; wait_synchronization(usart); /* 8 bits of data, no parity, 1 stop bit in normal mode */ usart->CTRLA.reg = cfg->pads | /* Internal clock */ SERCOM_USART_CTRLA_MODE_USART_INT_CLK #if defined(SERCOM_USART_CTRLA_SAMPR) /* 16x oversampling with arithmetic baud rate generation */ | SERCOM_USART_CTRLA_SAMPR(0) #endif | SERCOM_USART_CTRLA_FORM(0) | SERCOM_USART_CTRLA_CPOL | SERCOM_USART_CTRLA_DORD; wait_synchronization(usart); /* Enable receiver and transmitter */ usart->CTRLB.reg = SERCOM_SPI_CTRLB_CHSIZE(0) | SERCOM_USART_CTRLB_RXEN | SERCOM_USART_CTRLB_TXEN; wait_synchronization(usart); retval = uart_sam0_set_baudrate(usart, cfg->baudrate, SOC_ATMEL_SAM0_GCLK0_FREQ_HZ); if (retval != 0) { return retval; } #ifdef CONFIG_UART_INTERRUPT_DRIVEN cfg->irq_config_func(dev); #endif usart->CTRLA.bit.ENABLE = 1; wait_synchronization(usart); return 0; } static int uart_sam0_poll_in(struct device *dev, unsigned char *c) { SercomUsart *const usart = DEV_CFG(dev)->regs; if (!usart->INTFLAG.bit.RXC) { return -EBUSY; } *c = (unsigned char)usart->DATA.reg; return 0; } static unsigned char uart_sam0_poll_out(struct device *dev, unsigned char c) { SercomUsart *const usart = DEV_CFG(dev)->regs; while (!usart->INTFLAG.bit.DRE) { } /* send a character */ usart->DATA.reg = c; return c; } #if CONFIG_UART_INTERRUPT_DRIVEN static void uart_sam0_isr(void *arg) { struct device *dev = arg; struct uart_sam0_dev_data *const dev_data = DEV_DATA(dev); if (dev_data->cb) { dev_data->cb(dev); } } static int uart_sam0_fifo_fill(struct device *dev, const u8_t *tx_data, int len) { SercomUsart *regs = DEV_CFG(dev)->regs; if (regs->INTFLAG.bit.DRE && len >= 1) { regs->DATA.reg = tx_data[0]; return 1; } else { return 0; } } static void uart_sam0_irq_tx_enable(struct device *dev) { SercomUsart *regs = DEV_CFG(dev)->regs; regs->INTENSET.reg = SERCOM_USART_INTENCLR_DRE; } static void uart_sam0_irq_tx_disable(struct device *dev) { SercomUsart *const regs = DEV_CFG(dev)->regs; regs->INTENCLR.reg = SERCOM_USART_INTENCLR_DRE; } static int uart_sam0_irq_tx_ready(struct device *dev) { SercomUsart *const regs = DEV_CFG(dev)->regs; return regs->INTFLAG.bit.DRE != 0; } static void uart_sam0_irq_rx_enable(struct device *dev) { SercomUsart *const regs = DEV_CFG(dev)->regs; regs->INTENSET.reg = SERCOM_USART_INTENSET_RXC; } static void uart_sam0_irq_rx_disable(struct device *dev) { SercomUsart *const regs = DEV_CFG(dev)->regs; regs->INTENCLR.reg = SERCOM_USART_INTENCLR_RXC; } static int uart_sam0_irq_rx_ready(struct device *dev) { SercomUsart *const regs = DEV_CFG(dev)->regs; return regs->INTFLAG.bit.RXC != 0; } static int uart_sam0_fifo_read(struct device *dev, u8_t *rx_data, const int size) { SercomUsart *const regs = DEV_CFG(dev)->regs; if (regs->INTFLAG.bit.RXC) { u8_t ch = regs->DATA.reg; if (size >= 1) { *rx_data = ch; return 1; } else { return -EINVAL; } } return 0; } static int uart_sam0_irq_is_pending(struct device *dev) { SercomUsart *const regs = DEV_CFG(dev)->regs; return (regs->INTENSET.reg & regs->INTFLAG.reg) != 0; } static int uart_sam0_irq_update(struct device *dev) { return 1; } static void uart_sam0_irq_callback_set(struct device *dev, uart_irq_callback_t cb) { struct uart_sam0_dev_data *const dev_data = DEV_DATA(dev); dev_data->cb = cb; } #endif static const struct uart_driver_api uart_sam0_driver_api = { .poll_in = uart_sam0_poll_in, .poll_out = uart_sam0_poll_out, #if CONFIG_UART_INTERRUPT_DRIVEN .fifo_fill = uart_sam0_fifo_fill, .fifo_read = uart_sam0_fifo_read, .irq_tx_enable = uart_sam0_irq_tx_enable, .irq_tx_disable = uart_sam0_irq_tx_disable, .irq_tx_ready = uart_sam0_irq_tx_ready, .irq_rx_enable = uart_sam0_irq_rx_enable, .irq_rx_disable = uart_sam0_irq_rx_disable, .irq_rx_ready = uart_sam0_irq_rx_ready, .irq_is_pending = uart_sam0_irq_is_pending, .irq_update = uart_sam0_irq_update, .irq_callback_set = uart_sam0_irq_callback_set, #endif }; #if CONFIG_UART_INTERRUPT_DRIVEN #define UART_SAM0_IRQ_HANDLER_DECL(n) \ static void uart_sam0_irq_config_##n(struct device *dev) #define UART_SAM0_IRQ_HANDLER_FUNC(n) \ .irq_config_func = uart_sam0_irq_config_##n, #define UART_SAM0_IRQ_HANDLER(n) \ static void uart_sam0_irq_config_##n(struct device *dev) \ { \ IRQ_CONNECT(CONFIG_UART_SAM0_SERCOM##n##_IRQ, \ CONFIG_UART_SAM0_SERCOM##n##_IRQ_PRIORITY, \ uart_sam0_isr, DEVICE_GET(uart_sam0_##n), \ 0); \ irq_enable(CONFIG_UART_SAM0_SERCOM##n##_IRQ); \ } #else #define UART_SAM0_IRQ_HANDLER_DECL(n) #define UART_SAM0_IRQ_HANDLER_FUNC(n) #define UART_SAM0_IRQ_HANDLER(n) #endif #define UART_SAM0_CONFIG_DEFN(n) \ static const struct uart_sam0_dev_cfg uart_sam0_config_##n = { \ .regs = (SercomUsart *)CONFIG_UART_SAM0_SERCOM##n##_BASE_ADDRESS, \ .baudrate = CONFIG_UART_SAM0_SERCOM##n##_CURRENT_SPEED, \ .pm_apbcmask = PM_APBCMASK_SERCOM##n, \ .gclk_clkctrl_id = GCLK_CLKCTRL_ID_SERCOM##n##_CORE, \ .pads = CONFIG_UART_SAM0_SERCOM##n##_PADS, \ UART_SAM0_IRQ_HANDLER_FUNC(n) \ } #define UART_SAM0_DEVICE_INIT(n) \ static struct uart_sam0_dev_data uart_sam0_data_##n; \ UART_SAM0_IRQ_HANDLER_DECL(n); \ UART_SAM0_CONFIG_DEFN(n); \ DEVICE_AND_API_INIT(uart_sam0_##n, CONFIG_UART_SAM0_SERCOM##n##_LABEL, \ uart_sam0_init, &uart_sam0_data_##n, \ &uart_sam0_config_##n, PRE_KERNEL_1, \ CONFIG_KERNEL_INIT_PRIORITY_DEVICE, \ &uart_sam0_driver_api); \ UART_SAM0_IRQ_HANDLER(n) #if CONFIG_UART_SAM0_SERCOM0_BASE_ADDRESS UART_SAM0_DEVICE_INIT(0) #endif #if CONFIG_UART_SAM0_SERCOM1_BASE_ADDRESS UART_SAM0_DEVICE_INIT(1) #endif #if CONFIG_UART_SAM0_SERCOM2_BASE_ADDRESS UART_SAM0_DEVICE_INIT(2) #endif #if CONFIG_UART_SAM0_SERCOM3_BASE_ADDRESS UART_SAM0_DEVICE_INIT(3) #endif #if CONFIG_UART_SAM0_SERCOM4_BASE_ADDRESS UART_SAM0_DEVICE_INIT(4) #endif #if CONFIG_UART_SAM0_SERCOM5_BASE_ADDRESS UART_SAM0_DEVICE_INIT(5) #endif |