/* * Copyright (c) 2021, Yonatan Schachter * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include /* pico-sdk includes */ #include #include #include #include #define DT_DRV_COMPAT raspberrypi_pico_gpio #define ALL_EVENTS (GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE \ | GPIO_IRQ_LEVEL_LOW | GPIO_IRQ_LEVEL_HIGH) struct gpio_rpi_config { struct gpio_driver_config common; void (*bank_config_func)(void); }; struct gpio_rpi_data { struct gpio_driver_data common; sys_slist_t callbacks; uint32_t int_enabled_mask; }; static int gpio_rpi_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) { if (flags & GPIO_SINGLE_ENDED) { return -ENOTSUP; } /* Avoid gpio_init, since that also clears previously set direction/high/low */ gpio_set_function(pin, GPIO_FUNC_SIO); if (flags & GPIO_OUTPUT) { gpio_set_dir(pin, GPIO_OUT); if (flags & GPIO_OUTPUT_INIT_HIGH) { gpio_put(pin, 1); } else if (flags & GPIO_OUTPUT_INIT_LOW) { gpio_put(pin, 0); } } else if (flags & GPIO_INPUT) { gpio_set_dir(pin, GPIO_IN); gpio_set_pulls(pin, (flags & GPIO_PULL_UP) != 0U, (flags & GPIO_PULL_DOWN) != 0U); } return 0; } static int gpio_rpi_port_get_raw(const struct device *dev, uint32_t *value) { *value = gpio_get_all(); return 0; } static int gpio_rpi_port_set_masked_raw(const struct device *port, uint32_t mask, uint32_t value) { gpio_put_masked(mask, value); return 0; } static int gpio_rpi_port_set_bits_raw(const struct device *port, uint32_t pins) { gpio_set_mask(pins); return 0; } static int gpio_rpi_port_clear_bits_raw(const struct device *port, uint32_t pins) { gpio_clr_mask(pins); return 0; } static int gpio_rpi_port_toggle_bits(const struct device *port, uint32_t pins) { gpio_xor_mask(pins); return 0; } static int gpio_rpi_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, enum gpio_int_mode mode, enum gpio_int_trig trig) { struct gpio_rpi_data *data = dev->data; uint32_t events = 0; gpio_set_irq_enabled(pin, ALL_EVENTS, false); if (mode != GPIO_INT_DISABLE) { if (mode & GPIO_INT_EDGE) { if (trig & GPIO_INT_LOW_0) { events |= GPIO_IRQ_EDGE_FALL; } if (trig & GPIO_INT_HIGH_1) { events |= GPIO_IRQ_EDGE_RISE; } } else { if (trig & GPIO_INT_LOW_0) { events |= GPIO_IRQ_LEVEL_LOW; } if (trig & GPIO_INT_HIGH_1) { events |= GPIO_IRQ_LEVEL_HIGH; } } gpio_set_irq_enabled(pin, events, true); } WRITE_BIT(data->int_enabled_mask, pin, mode != GPIO_INT_DISABLE); return 0; } static int gpio_rpi_manage_callback(const struct device *dev, struct gpio_callback *callback, bool set) { struct gpio_rpi_data *data = dev->data; return gpio_manage_callback(&data->callbacks, callback, set); } static const struct gpio_driver_api gpio_rpi_driver_api = { .pin_configure = gpio_rpi_configure, .port_get_raw = gpio_rpi_port_get_raw, .port_set_masked_raw = gpio_rpi_port_set_masked_raw, .port_set_bits_raw = gpio_rpi_port_set_bits_raw, .port_clear_bits_raw = gpio_rpi_port_clear_bits_raw, .port_toggle_bits = gpio_rpi_port_toggle_bits, .pin_interrupt_configure = gpio_rpi_pin_interrupt_configure, .manage_callback = gpio_rpi_manage_callback, }; static void gpio_rpi_isr(const struct device *dev) { struct gpio_rpi_data *data = dev->data; io_irq_ctrl_hw_t *irq_ctrl_base; const io_rw_32 *status_reg; uint32_t events; uint32_t pin; irq_ctrl_base = &iobank0_hw->proc0_irq_ctrl; for (pin = 0; pin < NUM_BANK0_GPIOS; pin++) { status_reg = &irq_ctrl_base->ints[pin / 8]; events = (*status_reg >> 4 * (pin % 8)) & ALL_EVENTS; if (events) { gpio_acknowledge_irq(pin, ALL_EVENTS); gpio_fire_callbacks(&data->callbacks, dev, BIT(pin)); } } } static int gpio_rpi_bank_init(const struct device *dev) { const struct gpio_rpi_config *config = dev->config; config->bank_config_func(); return 0; } #define GPIO_RPI_INIT(idx) \ static void bank_##idx##_config_func(void) \ { \ IRQ_CONNECT(DT_INST_IRQN(idx), DT_INST_IRQ(idx, priority), \ gpio_rpi_isr, DEVICE_DT_INST_GET(idx), 0); \ irq_enable(DT_INST_IRQN(idx)); \ } \ static const struct gpio_rpi_config gpio_rpi_##idx##_config = { \ .bank_config_func = bank_##idx##_config_func, \ .common = \ { \ .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(idx), \ } \ }; \ \ static struct gpio_rpi_data gpio_rpi_##idx##_data; \ \ DEVICE_DT_INST_DEFINE(idx, gpio_rpi_bank_init, NULL, \ &gpio_rpi_##idx##_data, \ &gpio_rpi_##idx##_config, \ POST_KERNEL, CONFIG_GPIO_INIT_PRIORITY, \ &gpio_rpi_driver_api); DT_INST_FOREACH_STATUS_OKAY(GPIO_RPI_INIT)