gpio: Add QMSI-based implementation
Introduce the GPIO QMSI-based implementation. This is basically a shim layer that implement's Zephyr's GPIO APIs on top of QMSI drivers. This is an alternative driver that conflicts with the previous GPIO_DW implementation. In order to enable it you must set: - CONFIG_GPIO=n - CONFIG_GPIO_QMSI=y - CONFIG_GPIO_0=y - CONFIG_QMSI_DRIVERS=y - CONFIG_QMSI_INTALL_PATH="PATH_TO_QMSI" Note that this driver currently only supports one controller instance, GPIO_0. It is implemented this way due to a limitation from the current version of QMSI. QMSI versions later than 1.0 doesn't have this limitation. Missing: - support multiple controller instances (gpio_0, gpio_1, etc); - enable level triggered interrupts in sync with system clock, through setting INT_CLOCK_SYNC properly. Change-Id: Ib61b153dae9741806a9a31d7dc1f82b96d000fbe Signed-off-by: Jesus Sanchez-Palencia <jesus.sanchez-palencia@intel.com> Signed-off-by: Anas Nashif <anas.nashif@intel.com> Signed-off-by: Vinicius Costa Gomes <vinicius.gomes@intel.com>
This commit is contained in:
parent
41771e5b1c
commit
6504b7e72a
5 changed files with 347 additions and 2 deletions
|
@ -82,6 +82,7 @@ if GPIO
|
||||||
config GPIO_DW
|
config GPIO_DW
|
||||||
def_bool y
|
def_bool y
|
||||||
|
|
||||||
|
if GPIO_DW
|
||||||
config GPIO_DW_BOTHEDGES_SUPPORT
|
config GPIO_DW_BOTHEDGES_SUPPORT
|
||||||
def_bool y
|
def_bool y
|
||||||
config GPIO_DW_CLOCK_GATE
|
config GPIO_DW_CLOCK_GATE
|
||||||
|
@ -111,6 +112,22 @@ config GPIO_DW_1_BITS
|
||||||
default 6
|
default 6
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if QMSI_DRIVERS
|
||||||
|
config GPIO_QMSI
|
||||||
|
def_bool n
|
||||||
|
if GPIO_QMSI
|
||||||
|
config GPIO_QMSI_0
|
||||||
|
def_bool n
|
||||||
|
config GPIO_QMSI_0_NAME
|
||||||
|
default "gpio_0"
|
||||||
|
config GPIO_QMSI_0_IRQ
|
||||||
|
default 8
|
||||||
|
config GPIO_QMSI_0_PRI
|
||||||
|
default 2
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
if I2C
|
if I2C
|
||||||
config I2C_DW
|
config I2C_DW
|
||||||
def_bool y
|
def_bool y
|
||||||
|
|
|
@ -599,6 +599,40 @@ config GPIO_MMIO_1_OUTPUT
|
||||||
help
|
help
|
||||||
The memory address for output pin level register.
|
The memory address for output pin level register.
|
||||||
|
|
||||||
|
config GPIO_QMSI
|
||||||
|
bool "QMSI GPIO driver"
|
||||||
|
depends on GPIO && QMSI_DRIVERS
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enable the GPIO driver found on Intel Microcontroller
|
||||||
|
platforms, using the QMSI library.
|
||||||
|
|
||||||
|
config GPIO_QMSI_0
|
||||||
|
bool "QMSI GPIO block 0"
|
||||||
|
depends on GPIO_QMSI
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Include support for the GPIO port 0 using QMSI.
|
||||||
|
|
||||||
|
config GPIO_QMSI_0_NAME
|
||||||
|
string "Driver name"
|
||||||
|
depends on GPIO_QMSI_0
|
||||||
|
default "GPIO_0"
|
||||||
|
|
||||||
|
config GPIO_QMSI_0_IRQ
|
||||||
|
int "Controller interrupt number"
|
||||||
|
depends on GPIO_QMSI_0
|
||||||
|
default 0
|
||||||
|
help
|
||||||
|
IRQ number for the controller
|
||||||
|
|
||||||
|
config GPIO_QMSI_0_PRI
|
||||||
|
int "Controller interrupt priority"
|
||||||
|
depends on GPIO_QMSI_0
|
||||||
|
default 2
|
||||||
|
help
|
||||||
|
IRQ priority
|
||||||
|
|
||||||
config GPIO_SCH
|
config GPIO_SCH
|
||||||
bool "SCH GPIO controller"
|
bool "SCH GPIO controller"
|
||||||
depends on GPIO
|
depends on GPIO
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
ccflags-$(CONFIG_GPIO_QMSI) +=-I$(CONFIG_QMSI_INSTALL_PATH)/include
|
||||||
ccflags-y +=-I$(srctree)/drivers
|
ccflags-y +=-I$(srctree)/drivers
|
||||||
|
|
||||||
obj-$(CONFIG_GPIO_DW) += gpio_dw.o
|
obj-$(CONFIG_GPIO_DW) += gpio_dw.o
|
||||||
obj-$(CONFIG_GPIO_PCAL9535A) += gpio_pcal9535a.o
|
obj-$(CONFIG_GPIO_PCAL9535A) += gpio_pcal9535a.o
|
||||||
obj-$(CONFIG_GPIO_MMIO) += gpio_mmio.o
|
obj-$(CONFIG_GPIO_MMIO) += gpio_mmio.o
|
||||||
obj-$(CONFIG_GPIO_SCH) += gpio_sch.o
|
obj-$(CONFIG_GPIO_SCH) += gpio_sch.o
|
||||||
|
obj-$(CONFIG_GPIO_QMSI) += gpio_qmsi.o
|
||||||
|
|
284
drivers/gpio/gpio_qmsi.c
Normal file
284
drivers/gpio/gpio_qmsi.c
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Intel Corporation.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <device.h>
|
||||||
|
#include <drivers/ioapic.h>
|
||||||
|
#include <gpio.h>
|
||||||
|
#include <init.h>
|
||||||
|
#include <nanokernel.h>
|
||||||
|
#include <sys_io.h>
|
||||||
|
|
||||||
|
#include "qm_gpio.h"
|
||||||
|
#include "qm_scss.h"
|
||||||
|
|
||||||
|
#define INTEN 0x30
|
||||||
|
#define INTMASK 0x34
|
||||||
|
#define PORTA_EOI 0x4c
|
||||||
|
#define INT_GPIO_MASK 0x6c
|
||||||
|
|
||||||
|
struct gpio_qmsi_config {
|
||||||
|
qm_gpio_t gpio;
|
||||||
|
void *addr;
|
||||||
|
uint8_t num_pins;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gpio_qmsi_runtime {
|
||||||
|
gpio_callback_t callback;
|
||||||
|
uint32_t pin_callbacks;
|
||||||
|
uint8_t port_callback;
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
* TODO: Zephyr's API is not clear about the behavior of the this
|
||||||
|
* application callback. This topic is currently under
|
||||||
|
* discussion, so this implementation will be fixed as soon as a
|
||||||
|
* decision is made.
|
||||||
|
*/
|
||||||
|
static void gpio_qmsi_0_int_callback(uint32_t status)
|
||||||
|
{
|
||||||
|
struct device *port = device_get_binding(CONFIG_GPIO_QMSI_0_NAME);
|
||||||
|
struct gpio_qmsi_config *config = port->config->config_info;
|
||||||
|
struct gpio_qmsi_runtime *context = port->driver_data;
|
||||||
|
const uint32_t enabled_mask = context->pin_callbacks & status;
|
||||||
|
int bit;
|
||||||
|
|
||||||
|
if (!context->callback)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (context->port_callback) {
|
||||||
|
context->callback(port, status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!enabled_mask)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (bit = 0; bit < config->num_pins; bit++) {
|
||||||
|
if (enabled_mask & (1 << bit)) {
|
||||||
|
context->callback(port, bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qmsi_write_bit(uint32_t *target, uint8_t bit, uint8_t value)
|
||||||
|
{
|
||||||
|
if (value) {
|
||||||
|
sys_set_bit((uintptr_t) target, bit);
|
||||||
|
} else {
|
||||||
|
sys_clear_bit((uintptr_t) target, bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void qmsi_pin_config(struct device *port, uint32_t pin, int flags)
|
||||||
|
{
|
||||||
|
struct gpio_qmsi_config *gpio_config = port->config->config_info;
|
||||||
|
qm_gpio_reg_t *gpio_reg = gpio_config->addr;
|
||||||
|
qm_gpio_t gpio = gpio_config->gpio;
|
||||||
|
|
||||||
|
/* Save int mask and mask this pin while we configure the port.
|
||||||
|
* We do this to avoid "spurious interrupts", which is a behavior
|
||||||
|
* we have observed on QMSI and that still needs investigation.
|
||||||
|
*/
|
||||||
|
uint32_t intmask = gpio_reg->gpio_intmask;
|
||||||
|
qm_gpio_port_config_t cfg = { 0 };
|
||||||
|
|
||||||
|
sys_set_bit((uint32_t) gpio_reg + INTMASK, pin);
|
||||||
|
|
||||||
|
qm_gpio_get_config(gpio, &cfg);
|
||||||
|
|
||||||
|
qmsi_write_bit(&cfg.direction, pin, (flags & GPIO_DIR_MASK));
|
||||||
|
|
||||||
|
if (flags & GPIO_INT) {
|
||||||
|
qmsi_write_bit(&cfg.int_type, pin, (flags & GPIO_INT_EDGE));
|
||||||
|
qmsi_write_bit(&cfg.int_polarity, pin, (flags & GPIO_INT_ACTIVE_HIGH));
|
||||||
|
qmsi_write_bit(&cfg.int_debounce, pin, (flags & GPIO_INT_DEBOUNCE));
|
||||||
|
qmsi_write_bit(&cfg.int_bothedge, pin, (flags & GPIO_INT_DOUBLE_EDGE));
|
||||||
|
qmsi_write_bit(&cfg.int_en, pin, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* FIXME: for multiple GPIO ports */
|
||||||
|
cfg.callback = gpio_qmsi_0_int_callback;
|
||||||
|
qm_gpio_set_config(gpio, &cfg);
|
||||||
|
|
||||||
|
/* Recover the original interrupt mask for this port. */
|
||||||
|
sys_write32(intmask, (uint32_t) gpio_reg + INTMASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void qmsi_port_config(struct device *port, int flags)
|
||||||
|
{
|
||||||
|
struct gpio_qmsi_config *gpio_config = port->config->config_info;
|
||||||
|
uint8_t num_pins = gpio_config->num_pins;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < num_pins; i++) {
|
||||||
|
qmsi_pin_config(port, i, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int gpio_qmsi_config(struct device *port, int access_op,
|
||||||
|
uint32_t pin, int flags)
|
||||||
|
{
|
||||||
|
if (((flags & GPIO_INT) && (flags & GPIO_DIR_OUT)) ||
|
||||||
|
((flags & GPIO_DIR_IN) && (flags & GPIO_DIR_OUT))) {
|
||||||
|
return DEV_INVALID_CONF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (access_op == GPIO_ACCESS_BY_PIN) {
|
||||||
|
qmsi_pin_config(port, pin, flags);
|
||||||
|
} else {
|
||||||
|
qmsi_port_config(port, flags);
|
||||||
|
}
|
||||||
|
return DEV_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int gpio_qmsi_write(struct device *port, int access_op,
|
||||||
|
uint32_t pin, uint32_t value)
|
||||||
|
{
|
||||||
|
struct gpio_qmsi_config *gpio_config = port->config->config_info;
|
||||||
|
qm_gpio_t gpio = gpio_config->gpio;
|
||||||
|
|
||||||
|
if (access_op == GPIO_ACCESS_BY_PIN) {
|
||||||
|
if (value) {
|
||||||
|
qm_gpio_set_pin(gpio, pin);
|
||||||
|
} else {
|
||||||
|
qm_gpio_clear_pin(gpio, pin);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qm_gpio_write_port(gpio, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DEV_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int gpio_qmsi_read(struct device *port, int access_op,
|
||||||
|
uint32_t pin, uint32_t *value)
|
||||||
|
{
|
||||||
|
struct gpio_qmsi_config *gpio_config = port->config->config_info;
|
||||||
|
qm_gpio_t gpio = gpio_config->gpio;
|
||||||
|
|
||||||
|
if (access_op == GPIO_ACCESS_BY_PIN) {
|
||||||
|
*value = qm_gpio_read_pin(gpio, pin);
|
||||||
|
} else {
|
||||||
|
*value = qm_gpio_read_port(gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
return DEV_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int gpio_qmsi_set_callback(struct device *port,
|
||||||
|
gpio_callback_t callback)
|
||||||
|
{
|
||||||
|
struct gpio_qmsi_runtime *context = port->driver_data;
|
||||||
|
|
||||||
|
context->callback = callback;
|
||||||
|
|
||||||
|
return DEV_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int gpio_qmsi_enable_callback(struct device *port, int access_op,
|
||||||
|
uint32_t pin)
|
||||||
|
{
|
||||||
|
struct gpio_qmsi_config *gpio_config = port->config->config_info;
|
||||||
|
struct gpio_qmsi_runtime *context = port->driver_data;
|
||||||
|
uint32_t reg = (uint32_t) gpio_config->addr;
|
||||||
|
|
||||||
|
sys_set_bit(reg + PORTA_EOI, pin);
|
||||||
|
sys_clear_bit(reg + INTMASK, pin);
|
||||||
|
|
||||||
|
if (access_op == GPIO_ACCESS_BY_PIN) {
|
||||||
|
context->pin_callbacks |= BIT(pin);
|
||||||
|
} else {
|
||||||
|
context->port_callback = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DEV_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int gpio_qmsi_disable_callback(struct device *port, int access_op,
|
||||||
|
uint32_t pin)
|
||||||
|
{
|
||||||
|
struct gpio_qmsi_config *gpio_config = port->config->config_info;
|
||||||
|
struct gpio_qmsi_runtime *context = port->driver_data;
|
||||||
|
uint32_t reg = (uint32_t) gpio_config->addr;
|
||||||
|
|
||||||
|
sys_set_bit(reg + INTMASK, pin);
|
||||||
|
|
||||||
|
if (access_op == GPIO_ACCESS_BY_PIN) {
|
||||||
|
context->pin_callbacks &= ~BIT(pin);
|
||||||
|
} else {
|
||||||
|
context->port_callback = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DEV_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int gpio_qmsi_suspend_port(struct device *port)
|
||||||
|
{
|
||||||
|
return DEV_NO_SUPPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int gpio_qmsi_resume_port(struct device *port)
|
||||||
|
{
|
||||||
|
return DEV_NO_SUPPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct gpio_driver_api api_funcs = {
|
||||||
|
.config = gpio_qmsi_config,
|
||||||
|
.write = gpio_qmsi_write,
|
||||||
|
.read = gpio_qmsi_read,
|
||||||
|
.set_callback = gpio_qmsi_set_callback,
|
||||||
|
.enable_callback = gpio_qmsi_enable_callback,
|
||||||
|
.disable_callback = gpio_qmsi_disable_callback,
|
||||||
|
.suspend = gpio_qmsi_suspend_port,
|
||||||
|
.resume = gpio_qmsi_resume_port
|
||||||
|
};
|
||||||
|
|
||||||
|
int gpio_qmsi_init(struct device *port)
|
||||||
|
{
|
||||||
|
struct gpio_qmsi_config *gpio_config = port->config->config_info;
|
||||||
|
uint32_t reg = (uint32_t) gpio_config->addr;
|
||||||
|
|
||||||
|
clk_periph_enable(CLK_PERIPH_GPIO_REGISTER |
|
||||||
|
CLK_PERIPH_GPIO_INTERRUPT | CLK_PERIPH_GPIO_DB);
|
||||||
|
|
||||||
|
/* mask and disable interrupts */
|
||||||
|
sys_write32(~(0), reg + INTMASK);
|
||||||
|
sys_write32(0, reg + INTEN);
|
||||||
|
sys_write32(~(0), reg + PORTA_EOI);
|
||||||
|
|
||||||
|
irq_connect(CONFIG_GPIO_QMSI_0_IRQ, CONFIG_GPIO_QMSI_0_PRI, qm_gpio_isr_0,
|
||||||
|
0, IOAPIC_EDGE | IOAPIC_HIGH);
|
||||||
|
|
||||||
|
/* Enable GPIO IRQ and unmask interrupts for Lakemont. */
|
||||||
|
sys_clear_bit(QM_SCSS_INT_BASE + INT_GPIO_MASK, 0);
|
||||||
|
irq_enable(CONFIG_GPIO_QMSI_0_IRQ);
|
||||||
|
|
||||||
|
port->driver_api = &api_funcs;
|
||||||
|
return DEV_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct gpio_qmsi_config gpio_0_config = {
|
||||||
|
.gpio = QM_GPIO_0,
|
||||||
|
.addr = &QM_GPIO[0],
|
||||||
|
.num_pins = QM_NUM_GPIO_PINS,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct gpio_qmsi_runtime gpio_0_runtime;
|
||||||
|
|
||||||
|
DECLARE_DEVICE_INIT_CONFIG(gpio_0, CONFIG_GPIO_QMSI_0_NAME,
|
||||||
|
&gpio_qmsi_init, &gpio_0_config);
|
||||||
|
|
||||||
|
SYS_DEFINE_DEVICE(gpio_0, &gpio_0_runtime, SECONDARY,
|
||||||
|
CONFIG_KERNEL_INIT_PRIORITY_DEVICE);
|
|
@ -97,6 +97,14 @@
|
||||||
#define GPIO_NAME "GPIO_"
|
#define GPIO_NAME "GPIO_"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_GPIO_DW_0
|
||||||
|
#define GPIO_DRV_NAME CONFIG_GPIO_DW_0_NAME
|
||||||
|
#elif CONFIG_GPIO_QMSI_0
|
||||||
|
#define GPIO_DRV_NAME CONFIG_GPIO_QMSI_0_NAME
|
||||||
|
#else
|
||||||
|
#error "Unsupported GPIO driver"
|
||||||
|
#endif
|
||||||
|
|
||||||
void gpio_callback(struct device *port, uint32_t pin)
|
void gpio_callback(struct device *port, uint32_t pin)
|
||||||
{
|
{
|
||||||
PRINT(GPIO_NAME "%d triggered\n", pin);
|
PRINT(GPIO_NAME "%d triggered\n", pin);
|
||||||
|
@ -112,9 +120,9 @@ void main(void)
|
||||||
|
|
||||||
nano_timer_init(&timer, timer_data);
|
nano_timer_init(&timer, timer_data);
|
||||||
|
|
||||||
gpio_dev = device_get_binding(CONFIG_GPIO_DW_0_NAME);
|
gpio_dev = device_get_binding(GPIO_DRV_NAME);
|
||||||
if (!gpio_dev) {
|
if (!gpio_dev) {
|
||||||
PRINT("Cannot find %s!\n", CONFIG_GPIO_DW_0_NAME);
|
PRINT("Cannot find %s!\n", GPIO_DRV_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Setup GPIO output */
|
/* Setup GPIO output */
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue