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