1 /*
2 * Copyright (c) 2022, Yonatan Schachter
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/drivers/pinctrl.h>
8 #include <zephyr/drivers/uart.h>
9
10 #include <zephyr/drivers/misc/pio_rpi_pico/pio_rpi_pico.h>
11
12 #include <hardware/pio.h>
13 #include <hardware/clocks.h>
14
15 #define DT_DRV_COMPAT raspberrypi_pico_uart_pio
16
17 #define CYCLES_PER_BIT 8
18 #define SIDESET_BIT_COUNT 2
19
20 struct pio_uart_config {
21 const struct device *piodev;
22 const struct pinctrl_dev_config *pcfg;
23 const uint32_t tx_pin;
24 const uint32_t rx_pin;
25 uint32_t baudrate;
26 };
27
28 struct pio_uart_data {
29 size_t tx_sm;
30 size_t rx_sm;
31 };
32
33 RPI_PICO_PIO_DEFINE_PROGRAM(uart_tx, 0, 3,
34 /* .wrap_target */
35 0x9fa0, /* 0: pull block side 1 [7] */
36 0xf727, /* 1: set x, 7 side 0 [7] */
37 0x6001, /* 2: out pins, 1 */
38 0x0642, /* 3: jmp x--, 2 [6] */
39 /* .wrap */
40 );
41
42 RPI_PICO_PIO_DEFINE_PROGRAM(uart_rx, 0, 8,
43 /* .wrap_target */
44 0x2020, /* 0: wait 0 pin, 0 */
45 0xea27, /* 1: set x, 7 [10] */
46 0x4001, /* 2: in pins, 1 */
47 0x0642, /* 3: jmp x--, 2 [6] */
48 0x00c8, /* 4: jmp pin, 8 */
49 0xc014, /* 5: irq nowait 4 rel */
50 0x20a0, /* 6: wait 1 pin, 0 */
51 0x0000, /* 7: jmp 0 */
52 0x8020, /* 8: push block */
53 /* .wrap */
54 );
55
pio_uart_tx_init(PIO pio,uint32_t sm,uint32_t tx_pin,float div)56 static int pio_uart_tx_init(PIO pio, uint32_t sm, uint32_t tx_pin, float div)
57 {
58 uint32_t offset;
59 pio_sm_config sm_config;
60
61 if (!pio_can_add_program(pio, RPI_PICO_PIO_GET_PROGRAM(uart_tx))) {
62 return -EBUSY;
63 }
64
65 offset = pio_add_program(pio, RPI_PICO_PIO_GET_PROGRAM(uart_tx));
66 sm_config = pio_get_default_sm_config();
67
68 sm_config_set_sideset(&sm_config, SIDESET_BIT_COUNT, true, false);
69 sm_config_set_out_shift(&sm_config, true, false, 0);
70 sm_config_set_out_pins(&sm_config, tx_pin, 1);
71 sm_config_set_sideset_pins(&sm_config, tx_pin);
72 sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX);
73 sm_config_set_clkdiv(&sm_config, div);
74 sm_config_set_wrap(&sm_config,
75 offset + RPI_PICO_PIO_GET_WRAP_TARGET(uart_tx),
76 offset + RPI_PICO_PIO_GET_WRAP(uart_tx));
77
78 pio_sm_set_pins_with_mask(pio, sm, BIT(tx_pin), BIT(tx_pin));
79 pio_sm_set_pindirs_with_mask(pio, sm, BIT(tx_pin), BIT(tx_pin));
80 pio_sm_init(pio, sm, offset, &sm_config);
81 pio_sm_set_enabled(pio, sm, true);
82
83 return 0;
84 }
85
pio_uart_rx_init(PIO pio,uint32_t sm,uint32_t rx_pin,float div)86 static int pio_uart_rx_init(PIO pio, uint32_t sm, uint32_t rx_pin, float div)
87 {
88 pio_sm_config sm_config;
89 uint32_t offset;
90
91 if (!pio_can_add_program(pio, RPI_PICO_PIO_GET_PROGRAM(uart_rx))) {
92 return -EBUSY;
93 }
94
95 offset = pio_add_program(pio, RPI_PICO_PIO_GET_PROGRAM(uart_rx));
96 sm_config = pio_get_default_sm_config();
97
98 pio_sm_set_consecutive_pindirs(pio, sm, rx_pin, 1, false);
99 sm_config_set_in_pins(&sm_config, rx_pin);
100 sm_config_set_jmp_pin(&sm_config, rx_pin);
101 sm_config_set_in_shift(&sm_config, true, false, 0);
102 sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_RX);
103 sm_config_set_clkdiv(&sm_config, div);
104 sm_config_set_wrap(&sm_config,
105 offset + RPI_PICO_PIO_GET_WRAP_TARGET(uart_rx),
106 offset + RPI_PICO_PIO_GET_WRAP(uart_rx));
107
108 pio_sm_init(pio, sm, offset, &sm_config);
109 pio_sm_set_enabled(pio, sm, true);
110
111 return 0;
112 }
113
pio_uart_poll_in(const struct device * dev,unsigned char * c)114 static int pio_uart_poll_in(const struct device *dev, unsigned char *c)
115 {
116 const struct pio_uart_config *config = dev->config;
117 PIO pio = pio_rpi_pico_get_pio(config->piodev);
118 struct pio_uart_data *data = dev->data;
119 io_rw_8 *uart_rx_fifo_msb;
120
121 /*
122 * The rx FIFO is 4 bytes wide, add 3 to get the most significant
123 * byte.
124 */
125 uart_rx_fifo_msb = (io_rw_8 *)&pio->rxf[data->rx_sm] + 3;
126 if (pio_sm_is_rx_fifo_empty(pio, data->rx_sm)) {
127 return -1;
128 }
129
130 /* Accessing the FIFO pops the read word from it */
131 *c = (char)*uart_rx_fifo_msb;
132 return 0;
133 }
134
pio_uart_poll_out(const struct device * dev,unsigned char c)135 static void pio_uart_poll_out(const struct device *dev, unsigned char c)
136 {
137 const struct pio_uart_config *config = dev->config;
138 struct pio_uart_data *data = dev->data;
139
140 pio_sm_put_blocking(pio_rpi_pico_get_pio(config->piodev), data->tx_sm, (uint32_t)c);
141 }
142
pio_uart_init(const struct device * dev)143 static int pio_uart_init(const struct device *dev)
144 {
145 const struct pio_uart_config *config = dev->config;
146 struct pio_uart_data *data = dev->data;
147 float sm_clock_div;
148 size_t tx_sm;
149 size_t rx_sm;
150 int retval;
151 PIO pio;
152
153 pio = pio_rpi_pico_get_pio(config->piodev);
154 sm_clock_div = (float)clock_get_hz(clk_sys) / (CYCLES_PER_BIT * config->baudrate);
155
156 retval = pio_rpi_pico_allocate_sm(config->piodev, &tx_sm);
157 retval |= pio_rpi_pico_allocate_sm(config->piodev, &rx_sm);
158
159 if (retval < 0) {
160 return retval;
161 }
162
163 data->tx_sm = tx_sm;
164 data->rx_sm = rx_sm;
165
166 retval = pio_uart_tx_init(pio, tx_sm, config->tx_pin, sm_clock_div);
167 if (retval < 0) {
168 return retval;
169 }
170
171 retval = pio_uart_rx_init(pio, rx_sm, config->rx_pin, sm_clock_div);
172 if (retval < 0) {
173 return retval;
174 }
175
176 return pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
177 }
178
179 static DEVICE_API(uart, pio_uart_driver_api) = {
180 .poll_in = pio_uart_poll_in,
181 .poll_out = pio_uart_poll_out,
182 };
183
184 #define PIO_UART_INIT(idx) \
185 PINCTRL_DT_INST_DEFINE(idx); \
186 static const struct pio_uart_config pio_uart##idx##_config = { \
187 .piodev = DEVICE_DT_GET(DT_INST_PARENT(idx)), \
188 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \
189 .tx_pin = DT_INST_RPI_PICO_PIO_PIN_BY_NAME(idx, default, 0, tx_pins, 0), \
190 .rx_pin = DT_INST_RPI_PICO_PIO_PIN_BY_NAME(idx, default, 0, rx_pins, 0), \
191 .baudrate = DT_INST_PROP(idx, current_speed), \
192 }; \
193 static struct pio_uart_data pio_uart##idx##_data; \
194 \
195 DEVICE_DT_INST_DEFINE(idx, pio_uart_init, NULL, &pio_uart##idx##_data, \
196 &pio_uart##idx##_config, POST_KERNEL, \
197 CONFIG_SERIAL_INIT_PRIORITY, \
198 &pio_uart_driver_api);
199
200 DT_INST_FOREACH_STATUS_OKAY(PIO_UART_INIT)
201