|  | /* | 
|  | * Copyright (c) 2016 Linaro Ltd. | 
|  | * | 
|  | * SPDX-License-Identifier: Apache-2.0 | 
|  | */ | 
|  |  | 
|  | /** | 
|  | * @file | 
|  | * @brief Driver to provide the GPIO API for a simple 32-bit i/o register | 
|  | * | 
|  | * This is a driver for accessing a simple, fixed purpose, 32-bit | 
|  | * memory-mapped i/o register using the same APIs as GPIO drivers. This is | 
|  | * useful when an SoC or board has registers that aren't part of a GPIO IP | 
|  | * block and these registers are used to control things that Zephyr normally | 
|  | * expects to be specified using a GPIO pin, e.g. for driving an LED, or | 
|  | * chip-select line for an SPI device. | 
|  | * | 
|  | * The implementation expects that all bits of the hardware register are both | 
|  | * readable and writable, and that for any bits that act as outputs, the value | 
|  | * read will have the value that was last written to it. This requirement | 
|  | * stems from the use of a read-modify-write method for all changes. | 
|  | * | 
|  | * It is possible to specify a restricted mask of bits that are valid for | 
|  | * access, and whenever the register is written, the value of bits outside this | 
|  | * mask will be preserved, even when the whole port is written to using | 
|  | * gpio_port_write. | 
|  | */ | 
|  |  | 
|  | #include <drivers/gpio/gpio_mmio32.h> | 
|  | #include <errno.h> | 
|  |  | 
|  | static int gpio_mmio32_config(const struct device *dev, | 
|  | gpio_pin_t pin, gpio_flags_t flags) | 
|  | { | 
|  | struct gpio_mmio32_context *context = dev->data; | 
|  | const struct gpio_mmio32_config *config = context->config; | 
|  |  | 
|  | if ((config->mask & (1 << pin)) == 0) { | 
|  | return -EINVAL; /* Pin not in our validity mask */ | 
|  | } | 
|  |  | 
|  | if (flags & ~(GPIO_INPUT | GPIO_OUTPUT | | 
|  | GPIO_OUTPUT_INIT_LOW | GPIO_OUTPUT_INIT_HIGH | | 
|  | GPIO_ACTIVE_LOW)) { | 
|  | /* We ignore direction and fake polarity, rest is unsupported */ | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | if ((flags & GPIO_OUTPUT) != 0) { | 
|  | unsigned int key; | 
|  | volatile uint32_t *reg = config->reg; | 
|  |  | 
|  | key = irq_lock(); | 
|  | if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) { | 
|  | *reg = (*reg | (1 << pin)); | 
|  | } else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) { | 
|  | *reg = (*reg & (config->mask & ~(1 << pin))); | 
|  | } | 
|  | irq_unlock(key); | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int gpio_mmio32_port_get_raw(const struct device *dev, uint32_t *value) | 
|  | { | 
|  | struct gpio_mmio32_context *context = dev->data; | 
|  | const struct gpio_mmio32_config *config = context->config; | 
|  |  | 
|  | *value = *config->reg & config->mask; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int gpio_mmio32_port_set_masked_raw(const struct device *dev, | 
|  | uint32_t mask, | 
|  | uint32_t value) | 
|  | { | 
|  | struct gpio_mmio32_context *context = dev->data; | 
|  | const struct gpio_mmio32_config *config = context->config; | 
|  | volatile uint32_t *reg = config->reg; | 
|  | unsigned int key; | 
|  |  | 
|  | mask &= config->mask; | 
|  | value &= mask; | 
|  |  | 
|  | /* Update pin state atomically */ | 
|  | key = irq_lock(); | 
|  | *reg = (*reg & ~mask) | value; | 
|  | irq_unlock(key); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int gpio_mmio32_port_set_bits_raw(const struct device *dev, | 
|  | uint32_t mask) | 
|  | { | 
|  | struct gpio_mmio32_context *context = dev->data; | 
|  | const struct gpio_mmio32_config *config = context->config; | 
|  | volatile uint32_t *reg = config->reg; | 
|  | unsigned int key; | 
|  |  | 
|  | mask &= config->mask; | 
|  |  | 
|  | /* Update pin state atomically */ | 
|  | key = irq_lock(); | 
|  | *reg = (*reg | mask); | 
|  | irq_unlock(key); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int gpio_mmio32_port_clear_bits_raw(const struct device *dev, | 
|  | uint32_t mask) | 
|  | { | 
|  | struct gpio_mmio32_context *context = dev->data; | 
|  | const struct gpio_mmio32_config *config = context->config; | 
|  | volatile uint32_t *reg = config->reg; | 
|  | unsigned int key; | 
|  |  | 
|  | mask &= config->mask; | 
|  |  | 
|  | /* Update pin state atomically */ | 
|  | key = irq_lock(); | 
|  | *reg = (*reg & ~mask); | 
|  | irq_unlock(key); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int gpio_mmio32_port_toggle_bits(const struct device *dev, | 
|  | uint32_t mask) | 
|  | { | 
|  | struct gpio_mmio32_context *context = dev->data; | 
|  | const struct gpio_mmio32_config *config = context->config; | 
|  | volatile uint32_t *reg = config->reg; | 
|  | unsigned int key; | 
|  |  | 
|  | mask &= config->mask; | 
|  |  | 
|  | /* Update pin state atomically */ | 
|  | key = irq_lock(); | 
|  | *reg = (*reg ^ mask); | 
|  | irq_unlock(key); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int gpio_mmio32_pin_interrupt_configure(const struct device *dev, | 
|  | gpio_pin_t pin, | 
|  | enum gpio_int_mode mode, | 
|  | enum gpio_int_trig trig) | 
|  | { | 
|  | if (mode != GPIO_INT_MODE_DISABLED) { | 
|  | return -ENOTSUP; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | const struct gpio_driver_api gpio_mmio32_api = { | 
|  | .pin_configure = gpio_mmio32_config, | 
|  | .port_get_raw = gpio_mmio32_port_get_raw, | 
|  | .port_set_masked_raw = gpio_mmio32_port_set_masked_raw, | 
|  | .port_set_bits_raw = gpio_mmio32_port_set_bits_raw, | 
|  | .port_clear_bits_raw = gpio_mmio32_port_clear_bits_raw, | 
|  | .port_toggle_bits = gpio_mmio32_port_toggle_bits, | 
|  | .pin_interrupt_configure = gpio_mmio32_pin_interrupt_configure, | 
|  | }; | 
|  |  | 
|  | int gpio_mmio32_init(const struct device *dev) | 
|  | { | 
|  | struct gpio_mmio32_context *context = dev->data; | 
|  | const struct gpio_mmio32_config *config = dev->config; | 
|  |  | 
|  | context->config = config; | 
|  |  | 
|  | return 0; | 
|  | } |