/* * Copyright (c) 2016 Jean-Paul Etienne * * SPDX-License-Identifier: Apache-2.0 */ /** * @file Driver for the pulpino GPIO controller. */ #include #include #include #include #include #include #include "gpio_utils.h" typedef void (*pulpino_cfg_func_t)(void); /* pulpino GPIO register-set structure */ struct gpio_pulpino_t { uint32_t paddir; uint32_t padin; uint32_t padout; uint32_t inten; uint32_t inttype0; uint32_t inttype1; uint32_t intstatus; }; struct gpio_pulpino_config { uint32_t gpio_base_addr; pulpino_cfg_func_t gpio_cfg_func; }; struct gpio_pulpino_data { /* list of callbacks */ sys_slist_t cb; }; /* Helper Macros for GPIO */ #define DEV_GPIO_CFG(dev) \ ((const struct gpio_pulpino_config * const)(dev)->config->config_info) #define DEV_GPIO(dev) \ ((volatile struct gpio_pulpino_t *)(DEV_GPIO_CFG(dev))->gpio_base_addr) #define DEV_GPIO_DATA(dev) \ ((struct gpio_pulpino_data *)(dev)->driver_data) static void gpio_pulpino_irq_handler(void *arg) { struct device *dev = (struct device *)arg; struct gpio_pulpino_data *data = DEV_GPIO_DATA(dev); volatile struct gpio_pulpino_t *gpio = DEV_GPIO(dev); _gpio_fire_callbacks(&data->cb, dev, gpio->intstatus); } /** * @brief Configure pin * * @param dev Device structure * @param access_op Access operation * @param pin The pin number * @param flags Flags of pin or port * * @return 0 if successful, failed otherwise */ static int gpio_pulpino_config(struct device *dev, int access_op, uint32_t pin, int flags) { volatile struct gpio_pulpino_t *gpio = DEV_GPIO(dev); if (access_op != GPIO_ACCESS_BY_PIN) return -ENOTSUP; if (pin > 31) return -ENODEV; /* Configure pin as gpio */ PULP_PADMUX |= (PULP_PAD_GPIO << pin); /* Configure gpio direction */ if (flags & GPIO_DIR_OUT) gpio->paddir |= BIT(pin); else gpio->paddir &= ~BIT(pin); /* * Configure interrupt if GPIO_INT is set. * Here, we just configure the gpio interrupt behavior, * we do not enable/disable interrupt for a particular * gpio. * Interrupt for a gpio is: * 1) enabled only via a call to gpio_pulpino_enable_callback. * 2) disabled only via a call to gpio_pulpino_disabled_callback. */ if (!(flags & GPIO_INT)) return 0; /* * Interrupt cannot be set for GPIO_DIR_OUT */ if (flags & GPIO_DIR_OUT) return -EINVAL; /* Double edge trigger not supported */ if (flags & GPIO_INT_DOUBLE_EDGE) return -ENOTSUP; /* Edge or Level triggered ? */ if (flags & GPIO_INT_EDGE) gpio->inttype1 |= BIT(pin); else gpio->inttype1 &= ~BIT(pin); /* Level High/Rising Edge ? */ if (flags & GPIO_INT_ACTIVE_HIGH) gpio->inttype0 &= ~BIT(pin); else gpio->inttype0 |= BIT(pin); return 0; } /** * @brief Set the pin * * @param dev Device struct * @param access_op Access operation * @param pin The pin number * @param value Value to set (0 or 1) * * @return 0 if successful, failed otherwise */ static int gpio_pulpino_write(struct device *dev, int access_op, uint32_t pin, uint32_t value) { volatile struct gpio_pulpino_t *gpio = DEV_GPIO(dev); if (access_op != GPIO_ACCESS_BY_PIN) return -ENOTSUP; if (value) gpio->padout |= BIT(pin); else gpio->padout &= ~BIT(pin); return 0; } /** * @brief Read the pin * * @param dev Device struct * @param access_op Access operation * @param pin The pin number * @param value Value of input pin(s) * * @return 0 if successful, failed otherwise */ static int gpio_pulpino_read(struct device *dev, int access_op, uint32_t pin, uint32_t *value) { volatile struct gpio_pulpino_t *gpio = DEV_GPIO(dev); if (access_op != GPIO_ACCESS_BY_PIN) return -ENOTSUP; /* * If gpio is configured as output, * read gpio value from padout register, * otherwise read gpio value from padin register */ if (gpio->paddir & BIT(pin)) *value = !!(gpio->padout & BIT(pin)); else *value = !!(gpio->padin & BIT(pin)); return 0; } static int gpio_pulpino_manage_callback(struct device *dev, struct gpio_callback *callback, bool set) { struct gpio_pulpino_data *data = DEV_GPIO_DATA(dev); _gpio_manage_callback(&data->cb, callback, set); return 0; } static int gpio_pulpino_enable_callback(struct device *dev, int access_op, uint32_t pin) { volatile struct gpio_pulpino_t *gpio = DEV_GPIO(dev); if (access_op != GPIO_ACCESS_BY_PIN) return -ENOTSUP; /* Enable interrupt for pin */ gpio->inten |= BIT(pin); return 0; } static int gpio_pulpino_disable_callback(struct device *dev, int access_op, uint32_t pin) { volatile struct gpio_pulpino_t *gpio = DEV_GPIO(dev); if (access_op != GPIO_ACCESS_BY_PIN) return -ENOTSUP; /* Disable interrupt for pin */ gpio->inten &= ~BIT(pin); return 0; } static const struct gpio_driver_api gpio_pulpino_driver = { .config = gpio_pulpino_config, .write = gpio_pulpino_write, .read = gpio_pulpino_read, .manage_callback = gpio_pulpino_manage_callback, .enable_callback = gpio_pulpino_enable_callback, .disable_callback = gpio_pulpino_disable_callback, }; /** * @brief Initialize a GPIO controller * * Perform basic initialization of a GPIO controller * * @param dev GPIO device struct * * @return 0 */ static int gpio_pulpino_init(struct device *dev) { const struct gpio_pulpino_config *cfg = DEV_GPIO_CFG(dev); cfg->gpio_cfg_func(); return 0; } static void gpio_pulpino_cfg_0(void); static const struct gpio_pulpino_config gpio_pulpino_config0 = { .gpio_base_addr = PULP_GPIO_0_BASE, .gpio_cfg_func = gpio_pulpino_cfg_0, }; static struct gpio_pulpino_data gpio_pulpino_data0; DEVICE_AND_API_INIT(gpio_pulpino_0, "gpio0", gpio_pulpino_init, &gpio_pulpino_data0, &gpio_pulpino_config0, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &gpio_pulpino_driver); static void gpio_pulpino_cfg_0(void) { IRQ_CONNECT(PULP_GPIO_0_IRQ, 0, gpio_pulpino_irq_handler, DEVICE_GET(gpio_pulpino_0), 0); irq_enable(PULP_GPIO_0_IRQ); }