diff --git a/boards/riscv/hifive1_revb/hifive1_revb.dts b/boards/riscv/hifive1_revb/hifive1_revb.dts index f46fba3aa9075..0c911ec6a60e8 100644 --- a/boards/riscv/hifive1_revb/hifive1_revb.dts +++ b/boards/riscv/hifive1_revb/hifive1_revb.dts @@ -24,15 +24,15 @@ leds { compatible = "gpio-leds"; led0: led_0 { - gpios = <&gpio0 19 0>; + gpios = <&gpio0 19 GPIO_ACTIVE_LOW>; label = "Green LED"; }; led1: led_1 { - gpios = <&gpio0 21 0>; + gpios = <&gpio0 21 GPIO_ACTIVE_LOW>; label = "Blue LED"; }; led2: led_2 { - gpios = <&gpio0 22 0>; + gpios = <&gpio0 22 GPIO_ACTIVE_LOW>; label = "Red LED"; }; }; diff --git a/drivers/gpio/gpio_sifive.c b/drivers/gpio/gpio_sifive.c index e4d1040915b9c..ae4a7ae33d807 100644 --- a/drivers/gpio/gpio_sifive.c +++ b/drivers/gpio/gpio_sifive.c @@ -61,34 +61,80 @@ struct gpio_sifive_data { #define DEV_GPIO_DATA(dev) \ ((struct gpio_sifive_data *)(dev)->driver_data) +/* _irq_level and _level2_irq are copied from + * soc/riscv/riscv-privileged/common/soc_common_irq.c + * Ideally this kind of thing should be made available in include/irq.h or + * somewhere similar since the multi-level IRQ format is generic to + Zephyr, and then both this copy and the one in riscv-privileged + * be removed for the shared implementation + */ +static inline unsigned int _irq_level(unsigned int irq) +{ + return ((irq >> 8) && 0xff) == 0U ? 1 : 2; +} + +static inline unsigned int _level2_irq(unsigned int irq) +{ + return (irq >> 8) - 1; +} + +/* Given gpio_irq_base and the pin number, return the IRQ number for the pin */ +static inline unsigned int gpio_sifive_pin_irq(unsigned int base_irq, int pin) +{ + unsigned int level = _irq_level(base_irq); + unsigned int pin_irq = 0; + + if (level == 1) { + pin_irq = base_irq + pin; + } else if (level == 2) { + pin_irq = base_irq + (pin << 8); + } + + return pin_irq; +} + +/* Given the PLIC source number, return the number of the GPIO pin associated + * with the interrupt + */ +static inline int gpio_sifive_plic_to_pin(unsigned int base_irq, int plic_irq) +{ + unsigned int level = _irq_level(base_irq); + + if (level == 2) { + base_irq = _level2_irq(base_irq); + } + + return (plic_irq - base_irq); +} + static void gpio_sifive_irq_handler(void *arg) { struct device *dev = (struct device *)arg; struct gpio_sifive_data *data = DEV_GPIO_DATA(dev); volatile struct gpio_sifive_t *gpio = DEV_GPIO(dev); const struct gpio_sifive_config *cfg = DEV_GPIO_CFG(dev); - int pin_mask; + int pin; /* Get the pin number generating the interrupt */ - pin_mask = 1 << (riscv_plic_get_irq() - cfg->gpio_irq_base); - - /* Call the corresponding callback registered for the pin */ - gpio_fire_callbacks(&data->cb, dev, pin_mask); + pin = gpio_sifive_plic_to_pin(cfg->gpio_irq_base, riscv_plic_get_irq()); /* * Write to either the rise_ip, fall_ip, high_ip or low_ip registers * to indicate to GPIO controller that interrupt for the corresponding * pin has been handled. */ - if (gpio->rise_ip & pin_mask) { - gpio->rise_ip = pin_mask; - } else if (gpio->fall_ip & pin_mask) { - gpio->fall_ip = pin_mask; - } else if (gpio->high_ip & pin_mask) { - gpio->high_ip = pin_mask; - } else if (gpio->low_ip & pin_mask) { - gpio->low_ip = pin_mask; + if (gpio->rise_ip & BIT(pin)) { + gpio->rise_ip = BIT(pin); + } else if (gpio->fall_ip & BIT(pin)) { + gpio->fall_ip = BIT(pin); + } else if (gpio->high_ip & BIT(pin)) { + gpio->high_ip = BIT(pin); + } else if (gpio->low_ip & BIT(pin)) { + gpio->low_ip = BIT(pin); } + + /* Call the corresponding callback registered for the pin */ + gpio_fire_callbacks(&data->cb, dev, BIT(pin)); } /** @@ -116,94 +162,33 @@ static int gpio_sifive_config(struct device *dev, return -EINVAL; } - /* Configure gpio direction */ - if (flags & GPIO_DIR_OUT) { - gpio->in_en &= ~BIT(pin); - gpio->out_en |= BIT(pin); - - /* - * Account for polarity only for GPIO_DIR_OUT. - * invert register handles only output gpios - */ - if (flags & GPIO_POL_INV) { - gpio->invert |= BIT(pin); - } else { - gpio->invert &= ~BIT(pin); - } - } else { - gpio->out_en &= ~BIT(pin); - gpio->in_en |= BIT(pin); - - /* Polarity inversion is not supported for input gpio */ - if (flags & GPIO_POL_INV) { - return -EINVAL; - } - - /* - * Pull-up can be configured only for input gpios. - * Only Pull-up can be enabled or disabled. - */ - if ((flags & GPIO_PUD_MASK) == GPIO_PUD_PULL_DOWN) { - return -EINVAL; - } - - if ((flags & GPIO_PUD_MASK) == GPIO_PUD_PULL_UP) { - gpio->pue |= BIT(pin); - } else { - gpio->pue &= ~BIT(pin); - } + /* We cannot support open-source open-drain configuration */ + if ((flags & GPIO_SINGLE_ENDED) != 0) { + return -ENOTSUP; } - /* - * 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_sifive_enable_callback. - * 2) disabled only via a call to gpio_sifive_disabled_callback. - */ - if (!(flags & GPIO_INT)) { - return 0; + /* We only support pull-ups, not pull-downs */ + if ((flags & GPIO_PULL_DOWN) != 0) { + return -ENOTSUP; } - /* - * Interrupt cannot be set for GPIO_DIR_OUT + /* Set pull-up if requested */ + WRITE_BIT(gpio->pue, pin, flags & GPIO_PULL_UP); + + /* Set the initial output value before enabling output to avoid + * glitches */ - if (flags & GPIO_DIR_OUT) { - return -EINVAL; + if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) { + gpio->out_val |= BIT(pin); } - - /* Edge or Level triggered ? */ - if (flags & GPIO_INT_EDGE) { - gpio->high_ie &= ~BIT(pin); - gpio->low_ie &= ~BIT(pin); - - /* Rising Edge, Falling Edge or Double Edge ? */ - if (flags & GPIO_INT_DOUBLE_EDGE) { - gpio->rise_ie |= BIT(pin); - gpio->fall_ie |= BIT(pin); - } else if (flags & GPIO_INT_ACTIVE_HIGH) { - gpio->rise_ie |= BIT(pin); - gpio->fall_ie &= ~BIT(pin); - } else { - gpio->rise_ie &= ~BIT(pin); - gpio->fall_ie |= BIT(pin); - } - } else { - gpio->rise_ie &= ~BIT(pin); - gpio->fall_ie &= ~BIT(pin); - - /* Level High ? */ - if (flags & GPIO_INT_ACTIVE_HIGH) { - gpio->high_ie |= BIT(pin); - gpio->low_ie &= ~BIT(pin); - } else { - gpio->high_ie &= ~BIT(pin); - gpio->low_ie |= BIT(pin); - } + if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) { + gpio->out_val &= ~BIT(pin); } + /* Enable input/output */ + WRITE_BIT(gpio->out_en, pin, flags & GPIO_OUTPUT); + WRITE_BIT(gpio->in_en, pin, flags & GPIO_INPUT); + return 0; } @@ -284,6 +269,129 @@ static int gpio_sifive_read(struct device *dev, return 0; } +static int gpio_sifive_port_get_raw(struct device *dev, + gpio_port_value_t *value) +{ + volatile struct gpio_sifive_t *gpio = DEV_GPIO(dev); + + *value = gpio->in_val; + + return 0; +} + +static int gpio_sifive_port_set_masked_raw(struct device *dev, + gpio_port_pins_t mask, + gpio_port_value_t value) +{ + volatile struct gpio_sifive_t *gpio = DEV_GPIO(dev); + + gpio->out_val = (gpio->out_val & ~mask) | (value & mask); + + return 0; +} + +static int gpio_sifive_port_set_bits_raw(struct device *dev, + gpio_port_pins_t mask) +{ + volatile struct gpio_sifive_t *gpio = DEV_GPIO(dev); + + gpio->out_val |= mask; + + return 0; +} + +static int gpio_sifive_port_clear_bits_raw(struct device *dev, + gpio_port_pins_t mask) +{ + volatile struct gpio_sifive_t *gpio = DEV_GPIO(dev); + + gpio->out_val &= ~mask; + + return 0; +} + +static int gpio_sifive_port_toggle_bits(struct device *dev, + gpio_port_pins_t mask) +{ + volatile struct gpio_sifive_t *gpio = DEV_GPIO(dev); + + gpio->out_val ^= mask; + + return 0; +} + +static int gpio_sifive_pin_interrupt_configure(struct device *dev, + unsigned int pin, + enum gpio_int_mode mode, + enum gpio_int_trig trig) +{ + volatile struct gpio_sifive_t *gpio = DEV_GPIO(dev); + const struct gpio_sifive_config *cfg = DEV_GPIO_CFG(dev); + + switch (mode) { + case GPIO_INT_MODE_DISABLED: + gpio->rise_ie &= ~BIT(pin); + gpio->fall_ie &= ~BIT(pin); + gpio->high_ie &= ~BIT(pin); + gpio->low_ie &= ~BIT(pin); + irq_disable(gpio_sifive_pin_irq(cfg->gpio_irq_base, pin)); + break; + case GPIO_INT_MODE_LEVEL: + /* TODO: The interrupt functionality of this driver is incomplete, + * but for the sake of not slowing down the GPIO API refactor, + * I'm just returning -ENOTSUP until we can track down the issue. + */ + return -ENOTSUP; + + gpio->rise_ie &= ~BIT(pin); + gpio->fall_ie &= ~BIT(pin); + + if (trig == GPIO_INT_TRIG_HIGH) { + gpio->high_ip = BIT(pin); + gpio->high_ie |= BIT(pin); + gpio->low_ie &= ~BIT(pin); + } else if (trig == GPIO_INT_TRIG_LOW) { + gpio->high_ie &= ~BIT(pin); + gpio->low_ip = BIT(pin); + gpio->low_ie |= BIT(pin); + } + irq_enable(gpio_sifive_pin_irq(cfg->gpio_irq_base, pin)); + break; + case GPIO_INT_MODE_EDGE: + /* TODO: The interrupt functionality of this driver is incomplete, + * but for the sake of not slowing down the GPIO API refactor, + * I'm just returning -ENOTSUP until we can track down the issue. + */ + return -ENOTSUP; + + gpio->high_ie &= ~BIT(pin); + gpio->low_ie &= ~BIT(pin); + + /* Rising Edge, Falling Edge or Double Edge ? */ + if (trig == GPIO_INT_TRIG_HIGH) { + gpio->rise_ip = BIT(pin); + gpio->rise_ie |= BIT(pin); + gpio->fall_ie &= ~BIT(pin); + } else if (trig == GPIO_INT_TRIG_LOW) { + gpio->rise_ie &= ~BIT(pin); + gpio->fall_ip = BIT(pin); + gpio->fall_ie |= BIT(pin); + } else { + gpio->rise_ip = BIT(pin); + gpio->rise_ie |= BIT(pin); + gpio->fall_ip = BIT(pin); + gpio->fall_ie |= BIT(pin); + } + irq_enable(gpio_sifive_pin_irq(cfg->gpio_irq_base, pin)); + break; + default: + __ASSERT(false, "Invalid MODE %d passed to driver", mode); + return -ENOTSUP; + } + + return 0; +} + static int gpio_sifive_manage_callback(struct device *dev, struct gpio_callback *callback, bool set) @@ -308,7 +416,7 @@ static int gpio_sifive_enable_callback(struct device *dev, } /* Enable interrupt for the pin at PLIC level */ - irq_enable(cfg->gpio_irq_base + pin); + irq_enable(gpio_sifive_pin_irq(cfg->gpio_irq_base, pin)); return 0; } @@ -328,18 +436,24 @@ static int gpio_sifive_disable_callback(struct device *dev, } /* Disable interrupt for the pin at PLIC level */ - irq_disable(cfg->gpio_irq_base + pin); + irq_disable(gpio_sifive_pin_irq(cfg->gpio_irq_base, pin)); return 0; } static const struct gpio_driver_api gpio_sifive_driver = { - .config = gpio_sifive_config, - .write = gpio_sifive_write, - .read = gpio_sifive_read, - .manage_callback = gpio_sifive_manage_callback, - .enable_callback = gpio_sifive_enable_callback, - .disable_callback = gpio_sifive_disable_callback, + .config = gpio_sifive_config, + .write = gpio_sifive_write, + .read = gpio_sifive_read, + .port_get_raw = gpio_sifive_port_get_raw, + .port_set_masked_raw = gpio_sifive_port_set_masked_raw, + .port_set_bits_raw = gpio_sifive_port_set_bits_raw, + .port_clear_bits_raw = gpio_sifive_port_clear_bits_raw, + .port_toggle_bits = gpio_sifive_port_toggle_bits, + .pin_interrupt_configure = gpio_sifive_pin_interrupt_configure, + .manage_callback = gpio_sifive_manage_callback, + .enable_callback = gpio_sifive_enable_callback, + .disable_callback = gpio_sifive_disable_callback, }; /** @@ -375,9 +489,9 @@ static int gpio_sifive_init(struct device *dev) static void gpio_sifive_cfg_0(void); static const struct gpio_sifive_config gpio_sifive_config0 = { - .gpio_base_addr = DT_INST_0_SIFIVE_GPIO0_BASE_ADDRESS, - .gpio_irq_base = DT_INST_0_SIFIVE_GPIO0_IRQ_0, - .gpio_cfg_func = gpio_sifive_cfg_0, + .gpio_base_addr = DT_INST_0_SIFIVE_GPIO0_BASE_ADDRESS, + .gpio_irq_base = DT_INST_0_SIFIVE_GPIO0_IRQ_0, + .gpio_cfg_func = gpio_sifive_cfg_0, }; static struct gpio_sifive_data gpio_sifive_data0;