201 lines
5.8 KiB
C
201 lines
5.8 KiB
C
/*
|
|
* Copyright (c) 2022, Yonatan Schachter
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/drivers/pinctrl.h>
|
|
#include <zephyr/drivers/uart.h>
|
|
|
|
#include <zephyr/drivers/misc/pio_rpi_pico/pio_rpi_pico.h>
|
|
|
|
#include <hardware/pio.h>
|
|
#include <hardware/clocks.h>
|
|
|
|
#define DT_DRV_COMPAT raspberrypi_pico_uart_pio
|
|
|
|
#define CYCLES_PER_BIT 8
|
|
#define SIDESET_BIT_COUNT 2
|
|
|
|
struct pio_uart_config {
|
|
const struct device *piodev;
|
|
const struct pinctrl_dev_config *pcfg;
|
|
const uint32_t tx_pin;
|
|
const uint32_t rx_pin;
|
|
uint32_t baudrate;
|
|
};
|
|
|
|
struct pio_uart_data {
|
|
size_t tx_sm;
|
|
size_t rx_sm;
|
|
};
|
|
|
|
RPI_PICO_PIO_DEFINE_PROGRAM(uart_tx, 0, 3,
|
|
/* .wrap_target */
|
|
0x9fa0, /* 0: pull block side 1 [7] */
|
|
0xf727, /* 1: set x, 7 side 0 [7] */
|
|
0x6001, /* 2: out pins, 1 */
|
|
0x0642, /* 3: jmp x--, 2 [6] */
|
|
/* .wrap */
|
|
);
|
|
|
|
RPI_PICO_PIO_DEFINE_PROGRAM(uart_rx, 0, 8,
|
|
/* .wrap_target */
|
|
0x2020, /* 0: wait 0 pin, 0 */
|
|
0xea27, /* 1: set x, 7 [10] */
|
|
0x4001, /* 2: in pins, 1 */
|
|
0x0642, /* 3: jmp x--, 2 [6] */
|
|
0x00c8, /* 4: jmp pin, 8 */
|
|
0xc014, /* 5: irq nowait 4 rel */
|
|
0x20a0, /* 6: wait 1 pin, 0 */
|
|
0x0000, /* 7: jmp 0 */
|
|
0x8020, /* 8: push block */
|
|
/* .wrap */
|
|
);
|
|
|
|
static int pio_uart_tx_init(PIO pio, uint32_t sm, uint32_t tx_pin, float div)
|
|
{
|
|
uint32_t offset;
|
|
pio_sm_config sm_config;
|
|
|
|
if (!pio_can_add_program(pio, RPI_PICO_PIO_GET_PROGRAM(uart_tx))) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
offset = pio_add_program(pio, RPI_PICO_PIO_GET_PROGRAM(uart_tx));
|
|
sm_config = pio_get_default_sm_config();
|
|
|
|
sm_config_set_sideset(&sm_config, SIDESET_BIT_COUNT, true, false);
|
|
sm_config_set_out_shift(&sm_config, true, false, 0);
|
|
sm_config_set_out_pins(&sm_config, tx_pin, 1);
|
|
sm_config_set_sideset_pins(&sm_config, tx_pin);
|
|
sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_TX);
|
|
sm_config_set_clkdiv(&sm_config, div);
|
|
sm_config_set_wrap(&sm_config,
|
|
offset + RPI_PICO_PIO_GET_WRAP_TARGET(uart_tx),
|
|
offset + RPI_PICO_PIO_GET_WRAP(uart_tx));
|
|
|
|
pio_sm_set_pins_with_mask(pio, sm, BIT(tx_pin), BIT(tx_pin));
|
|
pio_sm_set_pindirs_with_mask(pio, sm, BIT(tx_pin), BIT(tx_pin));
|
|
pio_sm_init(pio, sm, offset, &sm_config);
|
|
pio_sm_set_enabled(pio, sm, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pio_uart_rx_init(PIO pio, uint32_t sm, uint32_t rx_pin, float div)
|
|
{
|
|
pio_sm_config sm_config;
|
|
uint32_t offset;
|
|
|
|
if (!pio_can_add_program(pio, RPI_PICO_PIO_GET_PROGRAM(uart_rx))) {
|
|
return -EBUSY;
|
|
}
|
|
|
|
offset = pio_add_program(pio, RPI_PICO_PIO_GET_PROGRAM(uart_rx));
|
|
sm_config = pio_get_default_sm_config();
|
|
|
|
pio_sm_set_consecutive_pindirs(pio, sm, rx_pin, 1, false);
|
|
sm_config_set_in_pins(&sm_config, rx_pin);
|
|
sm_config_set_jmp_pin(&sm_config, rx_pin);
|
|
sm_config_set_in_shift(&sm_config, true, false, 0);
|
|
sm_config_set_fifo_join(&sm_config, PIO_FIFO_JOIN_RX);
|
|
sm_config_set_clkdiv(&sm_config, div);
|
|
sm_config_set_wrap(&sm_config,
|
|
offset + RPI_PICO_PIO_GET_WRAP_TARGET(uart_rx),
|
|
offset + RPI_PICO_PIO_GET_WRAP(uart_rx));
|
|
|
|
pio_sm_init(pio, sm, offset, &sm_config);
|
|
pio_sm_set_enabled(pio, sm, true);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int pio_uart_poll_in(const struct device *dev, unsigned char *c)
|
|
{
|
|
const struct pio_uart_config *config = dev->config;
|
|
PIO pio = pio_rpi_pico_get_pio(config->piodev);
|
|
struct pio_uart_data *data = dev->data;
|
|
io_rw_8 *uart_rx_fifo_msb;
|
|
|
|
/*
|
|
* The rx FIFO is 4 bytes wide, add 3 to get the most significant
|
|
* byte.
|
|
*/
|
|
uart_rx_fifo_msb = (io_rw_8 *)&pio->rxf[data->rx_sm] + 3;
|
|
if (pio_sm_is_rx_fifo_empty(pio, data->rx_sm)) {
|
|
return -1;
|
|
}
|
|
|
|
/* Accessing the FIFO pops the read word from it */
|
|
*c = (char)*uart_rx_fifo_msb;
|
|
return 0;
|
|
}
|
|
|
|
static void pio_uart_poll_out(const struct device *dev, unsigned char c)
|
|
{
|
|
const struct pio_uart_config *config = dev->config;
|
|
struct pio_uart_data *data = dev->data;
|
|
|
|
pio_sm_put_blocking(pio_rpi_pico_get_pio(config->piodev), data->tx_sm, (uint32_t)c);
|
|
}
|
|
|
|
static int pio_uart_init(const struct device *dev)
|
|
{
|
|
const struct pio_uart_config *config = dev->config;
|
|
struct pio_uart_data *data = dev->data;
|
|
float sm_clock_div;
|
|
size_t tx_sm;
|
|
size_t rx_sm;
|
|
int retval;
|
|
PIO pio;
|
|
|
|
pio = pio_rpi_pico_get_pio(config->piodev);
|
|
sm_clock_div = (float)clock_get_hz(clk_sys) / (CYCLES_PER_BIT * config->baudrate);
|
|
|
|
retval = pio_rpi_pico_allocate_sm(config->piodev, &tx_sm);
|
|
retval |= pio_rpi_pico_allocate_sm(config->piodev, &rx_sm);
|
|
|
|
if (retval < 0) {
|
|
return retval;
|
|
}
|
|
|
|
data->tx_sm = tx_sm;
|
|
data->rx_sm = rx_sm;
|
|
|
|
retval = pio_uart_tx_init(pio, tx_sm, config->tx_pin, sm_clock_div);
|
|
if (retval < 0) {
|
|
return retval;
|
|
}
|
|
|
|
retval = pio_uart_rx_init(pio, rx_sm, config->rx_pin, sm_clock_div);
|
|
if (retval < 0) {
|
|
return retval;
|
|
}
|
|
|
|
return pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
|
|
}
|
|
|
|
static const struct uart_driver_api pio_uart_driver_api = {
|
|
.poll_in = pio_uart_poll_in,
|
|
.poll_out = pio_uart_poll_out,
|
|
};
|
|
|
|
#define PIO_UART_INIT(idx) \
|
|
PINCTRL_DT_INST_DEFINE(idx); \
|
|
static const struct pio_uart_config pio_uart##idx##_config = { \
|
|
.piodev = DEVICE_DT_GET(DT_INST_PARENT(idx)), \
|
|
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \
|
|
.tx_pin = DT_INST_RPI_PICO_PIO_PIN_BY_NAME(idx, default, 0, tx_pins, 0), \
|
|
.rx_pin = DT_INST_RPI_PICO_PIO_PIN_BY_NAME(idx, default, 0, rx_pins, 0), \
|
|
.baudrate = DT_INST_PROP(idx, current_speed), \
|
|
}; \
|
|
static struct pio_uart_data pio_uart##idx##_data; \
|
|
\
|
|
DEVICE_DT_INST_DEFINE(idx, &pio_uart_init, NULL, &pio_uart##idx##_data, \
|
|
&pio_uart##idx##_config, POST_KERNEL, \
|
|
CONFIG_SERIAL_INIT_PRIORITY, \
|
|
&pio_uart_driver_api);
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(PIO_UART_INIT)
|