2015-11-27 08:24:36 -05:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2016-03-09 14:38:02 -03:00
|
|
|
#include <errno.h>
|
|
|
|
|
2015-11-27 08:24:36 -05:00
|
|
|
#include <device.h>
|
|
|
|
#include <drivers/ioapic.h>
|
|
|
|
#include <gpio.h>
|
|
|
|
#include <init.h>
|
|
|
|
#include <nanokernel.h>
|
|
|
|
#include <sys_io.h>
|
|
|
|
|
|
|
|
#include "qm_gpio.h"
|
2016-03-23 12:01:06 +01:00
|
|
|
#include "gpio_utils.h"
|
2016-05-18 07:09:16 -04:00
|
|
|
#include "qm_isr.h"
|
|
|
|
#include "clk.h"
|
2016-07-22 01:19:49 +01:00
|
|
|
#include <power.h>
|
2015-11-27 08:24:36 -05:00
|
|
|
|
|
|
|
struct gpio_qmsi_config {
|
|
|
|
qm_gpio_t gpio;
|
|
|
|
uint8_t num_pins;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct gpio_qmsi_runtime {
|
2016-03-23 12:01:06 +01:00
|
|
|
sys_slist_t callbacks;
|
2015-11-27 08:24:36 -05:00
|
|
|
uint32_t pin_callbacks;
|
2016-06-01 20:57:59 -07:00
|
|
|
#ifdef CONFIG_GPIO_QMSI_API_REENTRANCY
|
|
|
|
struct nano_sem sem;
|
|
|
|
#endif /* CONFIG_GPIO_QMSI_API_REENTRANCY */
|
2016-09-11 19:17:19 +03:00
|
|
|
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
|
|
|
uint32_t device_power_state;
|
|
|
|
#endif
|
2015-11-27 08:24:36 -05:00
|
|
|
};
|
2016-01-26 12:04:38 -02:00
|
|
|
|
2016-06-01 20:57:59 -07:00
|
|
|
#ifdef CONFIG_GPIO_QMSI_API_REENTRANCY
|
|
|
|
#define RP_GET(dev) (&((struct gpio_qmsi_runtime *)(dev->driver_data))->sem)
|
|
|
|
static const int reentrancy_protection = 1;
|
|
|
|
#else
|
|
|
|
#define RP_GET(context) (NULL)
|
|
|
|
static const int reentrancy_protection;
|
|
|
|
#endif /* CONFIG_GPIO_QMSI_API_REENTRANCY */
|
|
|
|
|
|
|
|
static void gpio_reentrancy_init(struct device *dev)
|
|
|
|
{
|
|
|
|
if (!reentrancy_protection) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nano_sem_init(RP_GET(dev));
|
|
|
|
nano_sem_give(RP_GET(dev));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gpio_critical_region_start(struct device *dev)
|
|
|
|
{
|
|
|
|
if (!reentrancy_protection) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nano_sem_take(RP_GET(dev), TICKS_UNLIMITED);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void gpio_critical_region_end(struct device *dev)
|
|
|
|
{
|
|
|
|
if (!reentrancy_protection) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nano_sem_give(RP_GET(dev));
|
|
|
|
}
|
|
|
|
|
2016-10-25 22:42:29 +01:00
|
|
|
static int gpio_qmsi_init(struct device *dev);
|
2016-01-26 12:04:38 -02:00
|
|
|
|
2016-09-11 19:17:19 +03:00
|
|
|
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
|
|
|
static void gpio_qmsi_set_power_state(struct device *dev, uint32_t power_state)
|
|
|
|
{
|
|
|
|
struct gpio_qmsi_runtime *context = dev->driver_data;
|
|
|
|
|
|
|
|
context->device_power_state = power_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t gpio_qmsi_get_power_state(struct device *dev)
|
|
|
|
{
|
|
|
|
struct gpio_qmsi_runtime *context = dev->driver_data;
|
|
|
|
|
|
|
|
return context->device_power_state;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define gpio_qmsi_set_power_state(...)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2016-01-26 12:04:38 -02:00
|
|
|
#ifdef CONFIG_GPIO_QMSI_0
|
2016-10-19 22:11:58 +01:00
|
|
|
static const struct gpio_qmsi_config gpio_0_config = {
|
2016-01-26 12:04:38 -02:00
|
|
|
.gpio = QM_GPIO_0,
|
|
|
|
.num_pins = QM_NUM_GPIO_PINS,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct gpio_qmsi_runtime gpio_0_runtime;
|
|
|
|
|
2016-07-22 01:19:49 +01:00
|
|
|
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
|
|
|
QM_RW uint32_t save_reg[10];
|
|
|
|
static uint32_t int_gpio_mask_save;
|
|
|
|
|
2016-09-11 19:17:19 +03:00
|
|
|
static int gpio_suspend_device(struct device *dev)
|
2016-07-22 01:19:49 +01:00
|
|
|
{
|
2016-10-18 19:23:19 -02:00
|
|
|
int_gpio_mask_save = REG_VAL(&QM_INTERRUPT_ROUTER->gpio_0_int_mask);
|
2016-09-11 19:17:19 +03:00
|
|
|
save_reg[0] = REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_swporta_dr);
|
|
|
|
save_reg[1] = REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_swporta_ddr);
|
|
|
|
save_reg[2] = REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_swporta_ctl);
|
|
|
|
save_reg[3] = REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_inten);
|
|
|
|
save_reg[4] = REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_intmask);
|
|
|
|
save_reg[5] = REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_inttype_level);
|
|
|
|
save_reg[6] = REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_int_polarity);
|
|
|
|
save_reg[7] = REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_debounce);
|
|
|
|
save_reg[8] = REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_ls_sync);
|
|
|
|
save_reg[9] = REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_int_bothedge);
|
|
|
|
|
|
|
|
gpio_qmsi_set_power_state(dev, DEVICE_PM_SUSPEND_STATE);
|
|
|
|
|
2016-07-22 01:19:49 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-11 19:17:19 +03:00
|
|
|
static int gpio_resume_device_from_suspend(struct device *dev)
|
2016-07-22 01:19:49 +01:00
|
|
|
{
|
2016-09-11 19:17:19 +03:00
|
|
|
REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_swporta_dr) = save_reg[0];
|
|
|
|
REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_swporta_ddr) = save_reg[1];
|
|
|
|
REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_swporta_ctl) = save_reg[2];
|
|
|
|
REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_inten) = save_reg[3];
|
|
|
|
REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_intmask) = save_reg[4];
|
|
|
|
REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_inttype_level) = save_reg[5];
|
|
|
|
REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_int_polarity) = save_reg[6];
|
|
|
|
REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_debounce) = save_reg[7];
|
|
|
|
REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_ls_sync) = save_reg[8];
|
|
|
|
REG_VAL(&QM_GPIO[QM_GPIO_0]->gpio_int_bothedge) = save_reg[9];
|
2016-10-18 19:23:19 -02:00
|
|
|
REG_VAL(&QM_INTERRUPT_ROUTER->gpio_0_int_mask) = int_gpio_mask_save;
|
2016-09-11 19:17:19 +03:00
|
|
|
|
|
|
|
gpio_qmsi_set_power_state(dev, DEVICE_PM_ACTIVE_STATE);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Implements the driver control management functionality
|
|
|
|
* the *context may include IN data or/and OUT data
|
|
|
|
*/
|
|
|
|
static int gpio_qmsi_device_ctrl(struct device *port, uint32_t ctrl_command,
|
|
|
|
void *context)
|
|
|
|
{
|
|
|
|
if (ctrl_command == DEVICE_PM_SET_POWER_STATE) {
|
|
|
|
if (*((uint32_t *)context) == DEVICE_PM_SUSPEND_STATE) {
|
|
|
|
return gpio_suspend_device(port);
|
|
|
|
} else if (*((uint32_t *)context) == DEVICE_PM_ACTIVE_STATE) {
|
|
|
|
return gpio_resume_device_from_suspend(port);
|
|
|
|
}
|
|
|
|
} else if (ctrl_command == DEVICE_PM_GET_POWER_STATE) {
|
|
|
|
*((uint32_t *)context) = gpio_qmsi_get_power_state(port);
|
|
|
|
return 0;
|
2016-07-22 01:19:49 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
2016-03-23 12:01:06 +01:00
|
|
|
|
2016-09-11 19:17:19 +03:00
|
|
|
DEVICE_DEFINE(gpio_0, CONFIG_GPIO_QMSI_0_NAME, &gpio_qmsi_init,
|
|
|
|
gpio_qmsi_device_ctrl, &gpio_0_runtime, &gpio_0_config,
|
|
|
|
SECONDARY, CONFIG_GPIO_QMSI_INIT_PRIORITY, NULL);
|
2016-07-22 01:19:49 +01:00
|
|
|
|
2016-01-26 12:04:38 -02:00
|
|
|
#endif /* CONFIG_GPIO_QMSI_0 */
|
|
|
|
|
2016-10-02 10:23:26 -04:00
|
|
|
#ifdef CONFIG_GPIO_QMSI_1
|
2016-10-19 22:11:58 +01:00
|
|
|
static const struct gpio_qmsi_config gpio_aon_config = {
|
2016-01-26 12:04:38 -02:00
|
|
|
.gpio = QM_AON_GPIO_0,
|
|
|
|
.num_pins = QM_NUM_AON_GPIO_PINS,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct gpio_qmsi_runtime gpio_aon_runtime;
|
|
|
|
|
2016-07-22 01:19:49 +01:00
|
|
|
|
|
|
|
#ifdef CONFIG_DEVICE_POWER_MANAGEMENT
|
|
|
|
static uint32_t int_gpio_aon_mask_save;
|
|
|
|
|
2016-09-11 19:17:19 +03:00
|
|
|
static int gpio_aon_suspend_device(struct device *dev)
|
2016-07-22 01:19:49 +01:00
|
|
|
{
|
2016-10-18 19:23:19 -02:00
|
|
|
int_gpio_aon_mask_save =
|
|
|
|
REG_VAL(&QM_INTERRUPT_ROUTER->aon_gpio_0_int_mask);
|
2016-09-11 19:17:19 +03:00
|
|
|
gpio_qmsi_set_power_state(dev, DEVICE_PM_SUSPEND_STATE);
|
2016-07-22 01:19:49 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-09-11 19:17:19 +03:00
|
|
|
static int gpio_aon_resume_device_from_suspend(struct device *dev)
|
2016-07-22 01:19:49 +01:00
|
|
|
{
|
2016-10-18 19:23:19 -02:00
|
|
|
REG_VAL(&QM_INTERRUPT_ROUTER->aon_gpio_0_int_mask) =
|
|
|
|
int_gpio_aon_mask_save;
|
2016-09-11 19:17:19 +03:00
|
|
|
gpio_qmsi_set_power_state(dev, DEVICE_PM_ACTIVE_STATE);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Implements the driver control management functionality
|
|
|
|
* the *context may include IN data or/and OUT data
|
|
|
|
*/
|
|
|
|
static int gpio_aon_device_ctrl(struct device *port, uint32_t ctrl_command,
|
|
|
|
void *context)
|
|
|
|
{
|
|
|
|
if (ctrl_command == DEVICE_PM_SET_POWER_STATE) {
|
|
|
|
if (*((uint32_t *)context) == DEVICE_PM_SUSPEND_STATE) {
|
|
|
|
return gpio_aon_suspend_device(port);
|
|
|
|
} else if (*((uint32_t *)context) == DEVICE_PM_ACTIVE_STATE) {
|
|
|
|
return gpio_aon_resume_device_from_suspend(port);
|
|
|
|
}
|
|
|
|
} else if (ctrl_command == DEVICE_PM_GET_POWER_STATE) {
|
|
|
|
*((uint32_t *)context) = gpio_qmsi_get_power_state(port);
|
|
|
|
return 0;
|
2016-07-22 01:19:49 +01:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-10-02 10:23:26 -04:00
|
|
|
DEVICE_DEFINE(gpio_aon, CONFIG_GPIO_QMSI_1_NAME, &gpio_qmsi_init,
|
2016-09-11 19:17:19 +03:00
|
|
|
gpio_aon_device_ctrl, &gpio_aon_runtime, &gpio_aon_config,
|
|
|
|
SECONDARY, CONFIG_GPIO_QMSI_INIT_PRIORITY, NULL);
|
2016-01-26 12:04:38 -02:00
|
|
|
|
2016-10-02 10:23:26 -04:00
|
|
|
#endif /* CONFIG_GPIO_QMSI_1 */
|
2016-01-26 12:04:38 -02:00
|
|
|
|
2015-11-27 08:24:36 -05:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2016-01-26 12:04:38 -02:00
|
|
|
static void gpio_qmsi_callback(struct device *port, uint32_t status)
|
2015-11-27 08:24:36 -05:00
|
|
|
{
|
|
|
|
struct gpio_qmsi_runtime *context = port->driver_data;
|
|
|
|
const uint32_t enabled_mask = context->pin_callbacks & status;
|
|
|
|
|
2016-03-23 12:01:06 +01:00
|
|
|
if (enabled_mask) {
|
|
|
|
_gpio_fire_callbacks(&context->callbacks, port, enabled_mask);
|
2015-11-27 08:24:36 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-18 07:09:16 -04:00
|
|
|
static void gpio_qmsi_0_int_callback(void *data, uint32_t status)
|
2016-01-26 12:04:38 -02:00
|
|
|
{
|
|
|
|
#ifndef CONFIG_GPIO_QMSI_0
|
|
|
|
return;
|
|
|
|
#else
|
|
|
|
struct device *port = DEVICE_GET(gpio_0);
|
|
|
|
|
|
|
|
gpio_qmsi_callback(port, status);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-10-02 10:23:26 -04:00
|
|
|
#ifdef CONFIG_GPIO_QMSI_1
|
2016-05-18 07:09:16 -04:00
|
|
|
static void gpio_qmsi_aon_int_callback(void *data, uint32_t status)
|
2016-01-26 12:04:38 -02:00
|
|
|
{
|
|
|
|
struct device *port = DEVICE_GET(gpio_aon);
|
|
|
|
|
|
|
|
gpio_qmsi_callback(port, status);
|
|
|
|
}
|
2016-10-02 10:23:26 -04:00
|
|
|
#endif /* CONFIG_GPIO_QMSI_1 */
|
2016-01-26 12:04:38 -02:00
|
|
|
|
2015-11-27 08:24:36 -05:00
|
|
|
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)
|
|
|
|
{
|
2016-10-06 20:45:29 +01:00
|
|
|
const struct gpio_qmsi_config *gpio_config = port->config->config_info;
|
2015-11-27 08:24:36 -05:00
|
|
|
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.
|
|
|
|
*/
|
|
|
|
qm_gpio_port_config_t cfg = { 0 };
|
|
|
|
|
2016-05-18 07:09:16 -04:00
|
|
|
cfg.direction = QM_GPIO[gpio]->gpio_swporta_ddr;
|
|
|
|
cfg.int_en = QM_GPIO[gpio]->gpio_inten;
|
|
|
|
cfg.int_type = QM_GPIO[gpio]->gpio_inttype_level;
|
|
|
|
cfg.int_polarity = QM_GPIO[gpio]->gpio_int_polarity;
|
|
|
|
cfg.int_debounce = QM_GPIO[gpio]->gpio_debounce;
|
|
|
|
cfg.int_bothedge = QM_GPIO[gpio]->gpio_int_bothedge;
|
2015-11-27 08:24:36 -05:00
|
|
|
|
|
|
|
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));
|
2016-03-23 10:53:04 +01:00
|
|
|
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));
|
2015-11-27 08:24:36 -05:00
|
|
|
qmsi_write_bit(&cfg.int_en, pin, 1);
|
|
|
|
}
|
|
|
|
|
2016-01-26 12:04:38 -02:00
|
|
|
switch (gpio) {
|
|
|
|
case QM_GPIO_0:
|
|
|
|
cfg.callback = gpio_qmsi_0_int_callback;
|
|
|
|
break;
|
2016-02-17 18:08:02 -02:00
|
|
|
|
2016-10-02 10:23:26 -04:00
|
|
|
#ifdef CONFIG_GPIO_QMSI_1
|
2016-01-26 12:04:38 -02:00
|
|
|
case QM_AON_GPIO_0:
|
|
|
|
cfg.callback = gpio_qmsi_aon_int_callback;
|
|
|
|
break;
|
2016-10-02 10:23:26 -04:00
|
|
|
#endif /* CONFIG_GPIO_QMSI_1 */
|
2016-02-17 18:08:02 -02:00
|
|
|
|
2016-01-26 12:04:38 -02:00
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-01 20:57:59 -07:00
|
|
|
gpio_critical_region_start(port);
|
2015-11-27 08:24:36 -05:00
|
|
|
qm_gpio_set_config(gpio, &cfg);
|
2016-06-01 20:57:59 -07:00
|
|
|
gpio_critical_region_end(port);
|
2015-11-27 08:24:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
static inline void qmsi_port_config(struct device *port, int flags)
|
|
|
|
{
|
2016-10-06 20:45:29 +01:00
|
|
|
const struct gpio_qmsi_config *gpio_config = port->config->config_info;
|
2015-11-27 08:24:36 -05:00
|
|
|
uint8_t num_pins = gpio_config->num_pins;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < num_pins; i++) {
|
|
|
|
qmsi_pin_config(port, i, flags);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-23 10:53:04 +01:00
|
|
|
static inline int gpio_qmsi_config(struct device *port,
|
|
|
|
int access_op, uint32_t pin, int flags)
|
2015-11-27 08:24:36 -05:00
|
|
|
{
|
|
|
|
if (((flags & GPIO_INT) && (flags & GPIO_DIR_OUT)) ||
|
|
|
|
((flags & GPIO_DIR_IN) && (flags & GPIO_DIR_OUT))) {
|
2016-03-09 15:22:04 -03:00
|
|
|
return -EINVAL;
|
2015-11-27 08:24:36 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (access_op == GPIO_ACCESS_BY_PIN) {
|
|
|
|
qmsi_pin_config(port, pin, flags);
|
|
|
|
} else {
|
|
|
|
qmsi_port_config(port, flags);
|
|
|
|
}
|
2016-03-09 14:01:20 -03:00
|
|
|
return 0;
|
2015-11-27 08:24:36 -05:00
|
|
|
}
|
|
|
|
|
2016-03-23 10:53:04 +01:00
|
|
|
static inline int gpio_qmsi_write(struct device *port,
|
|
|
|
int access_op, uint32_t pin, uint32_t value)
|
2015-11-27 08:24:36 -05:00
|
|
|
{
|
2016-10-06 20:45:29 +01:00
|
|
|
const struct gpio_qmsi_config *gpio_config = port->config->config_info;
|
2015-11-27 08:24:36 -05:00
|
|
|
qm_gpio_t gpio = gpio_config->gpio;
|
|
|
|
|
2016-06-01 20:57:59 -07:00
|
|
|
gpio_critical_region_start(port);
|
|
|
|
|
2015-11-27 08:24:36 -05:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-06-01 20:57:59 -07:00
|
|
|
gpio_critical_region_end(port);
|
2016-03-09 14:01:20 -03:00
|
|
|
return 0;
|
2015-11-27 08:24:36 -05:00
|
|
|
}
|
|
|
|
|
2016-03-23 10:53:04 +01:00
|
|
|
static inline int gpio_qmsi_read(struct device *port,
|
|
|
|
int access_op, uint32_t pin, uint32_t *value)
|
2015-11-27 08:24:36 -05:00
|
|
|
{
|
2016-10-06 20:45:29 +01:00
|
|
|
const struct gpio_qmsi_config *gpio_config = port->config->config_info;
|
2015-11-27 08:24:36 -05:00
|
|
|
qm_gpio_t gpio = gpio_config->gpio;
|
2016-05-18 07:09:16 -04:00
|
|
|
qm_gpio_state_t state;
|
2015-11-27 08:24:36 -05:00
|
|
|
|
|
|
|
if (access_op == GPIO_ACCESS_BY_PIN) {
|
2016-05-18 07:09:16 -04:00
|
|
|
qm_gpio_read_pin(gpio, pin, &state);
|
|
|
|
*value = state;
|
2015-11-27 08:24:36 -05:00
|
|
|
} else {
|
2016-06-02 15:19:58 -07:00
|
|
|
qm_gpio_read_port(gpio, (uint32_t *const) value);
|
2015-11-27 08:24:36 -05:00
|
|
|
}
|
|
|
|
|
2016-03-09 14:01:20 -03:00
|
|
|
return 0;
|
2015-11-27 08:24:36 -05:00
|
|
|
}
|
|
|
|
|
2016-03-23 12:01:06 +01:00
|
|
|
static inline int gpio_qmsi_manage_callback(struct device *port,
|
|
|
|
struct gpio_callback *callback,
|
|
|
|
bool set)
|
2015-11-27 08:24:36 -05:00
|
|
|
{
|
|
|
|
struct gpio_qmsi_runtime *context = port->driver_data;
|
|
|
|
|
2016-03-23 12:01:06 +01:00
|
|
|
_gpio_manage_callback(&context->callbacks, callback, set);
|
2015-11-27 08:24:36 -05:00
|
|
|
|
2016-03-09 14:01:20 -03:00
|
|
|
return 0;
|
2015-11-27 08:24:36 -05:00
|
|
|
}
|
|
|
|
|
2016-03-23 10:53:04 +01:00
|
|
|
static inline int gpio_qmsi_enable_callback(struct device *port,
|
|
|
|
int access_op, uint32_t pin)
|
2015-11-27 08:24:36 -05:00
|
|
|
{
|
|
|
|
struct gpio_qmsi_runtime *context = port->driver_data;
|
|
|
|
|
2016-06-01 20:57:59 -07:00
|
|
|
gpio_critical_region_start(port);
|
|
|
|
|
2015-11-27 08:24:36 -05:00
|
|
|
if (access_op == GPIO_ACCESS_BY_PIN) {
|
|
|
|
context->pin_callbacks |= BIT(pin);
|
|
|
|
} else {
|
2016-03-23 12:01:06 +01:00
|
|
|
context->pin_callbacks = 0xffffffff;
|
2015-11-27 08:24:36 -05:00
|
|
|
}
|
|
|
|
|
2016-06-01 20:57:59 -07:00
|
|
|
gpio_critical_region_end(port);
|
2016-03-09 14:01:20 -03:00
|
|
|
return 0;
|
2015-11-27 08:24:36 -05:00
|
|
|
}
|
|
|
|
|
2016-03-23 10:53:04 +01:00
|
|
|
static inline int gpio_qmsi_disable_callback(struct device *port,
|
|
|
|
int access_op, uint32_t pin)
|
2015-11-27 08:24:36 -05:00
|
|
|
{
|
|
|
|
struct gpio_qmsi_runtime *context = port->driver_data;
|
|
|
|
|
2016-06-01 20:57:59 -07:00
|
|
|
gpio_critical_region_start(port);
|
|
|
|
|
2015-11-27 08:24:36 -05:00
|
|
|
if (access_op == GPIO_ACCESS_BY_PIN) {
|
|
|
|
context->pin_callbacks &= ~BIT(pin);
|
|
|
|
} else {
|
2016-03-23 12:01:06 +01:00
|
|
|
context->pin_callbacks = 0;
|
2015-11-27 08:24:36 -05:00
|
|
|
}
|
|
|
|
|
2016-06-01 20:57:59 -07:00
|
|
|
gpio_critical_region_end(port);
|
2016-03-09 14:01:20 -03:00
|
|
|
return 0;
|
2015-11-27 08:24:36 -05:00
|
|
|
}
|
|
|
|
|
2016-10-24 08:23:04 +01:00
|
|
|
static const struct gpio_driver_api api_funcs = {
|
2015-11-27 08:24:36 -05:00
|
|
|
.config = gpio_qmsi_config,
|
|
|
|
.write = gpio_qmsi_write,
|
|
|
|
.read = gpio_qmsi_read,
|
2016-03-23 12:01:06 +01:00
|
|
|
.manage_callback = gpio_qmsi_manage_callback,
|
2015-11-27 08:24:36 -05:00
|
|
|
.enable_callback = gpio_qmsi_enable_callback,
|
|
|
|
.disable_callback = gpio_qmsi_disable_callback,
|
|
|
|
};
|
|
|
|
|
2016-10-25 22:42:29 +01:00
|
|
|
static int gpio_qmsi_init(struct device *port)
|
2015-11-27 08:24:36 -05:00
|
|
|
{
|
2016-10-06 20:45:29 +01:00
|
|
|
const struct gpio_qmsi_config *gpio_config = port->config->config_info;
|
2015-11-27 08:24:36 -05:00
|
|
|
|
2016-06-01 20:57:59 -07:00
|
|
|
gpio_reentrancy_init(port);
|
|
|
|
|
2016-01-26 12:04:38 -02:00
|
|
|
switch (gpio_config->gpio) {
|
|
|
|
case QM_GPIO_0:
|
|
|
|
clk_periph_enable(CLK_PERIPH_GPIO_REGISTER |
|
|
|
|
CLK_PERIPH_GPIO_INTERRUPT |
|
2016-03-18 17:14:48 -03:00
|
|
|
CLK_PERIPH_GPIO_DB |
|
|
|
|
CLK_PERIPH_CLK);
|
2016-10-18 19:23:19 -02:00
|
|
|
IRQ_CONNECT(QM_IRQ_GPIO_0_INT, CONFIG_GPIO_QMSI_0_IRQ_PRI,
|
|
|
|
qm_gpio_0_isr, 0, IOAPIC_LEVEL | IOAPIC_HIGH);
|
|
|
|
irq_enable(QM_IRQ_GPIO_0_INT);
|
|
|
|
QM_INTERRUPT_ROUTER->gpio_0_int_mask &= ~BIT(0);
|
2016-01-26 12:04:38 -02:00
|
|
|
break;
|
2016-10-02 10:23:26 -04:00
|
|
|
#ifdef CONFIG_GPIO_QMSI_1
|
2016-01-26 12:04:38 -02:00
|
|
|
case QM_AON_GPIO_0:
|
2016-10-18 19:23:19 -02:00
|
|
|
IRQ_CONNECT(QM_IRQ_AON_GPIO_0_INT,
|
|
|
|
CONFIG_GPIO_QMSI_1_IRQ_PRI, qm_aon_gpio_0_isr,
|
2016-01-26 12:04:38 -02:00
|
|
|
0, IOAPIC_LEVEL | IOAPIC_HIGH);
|
2016-10-18 19:23:19 -02:00
|
|
|
irq_enable(QM_IRQ_AON_GPIO_0_INT);
|
|
|
|
QM_INTERRUPT_ROUTER->aon_gpio_0_int_mask &= ~BIT(0);
|
2016-01-26 12:04:38 -02:00
|
|
|
break;
|
2016-10-02 10:23:26 -04:00
|
|
|
#endif /* CONFIG_GPIO_QMSI_1 */
|
2016-01-26 12:04:38 -02:00
|
|
|
default:
|
2016-03-09 14:38:02 -03:00
|
|
|
return -EIO;
|
2016-01-26 12:04:38 -02:00
|
|
|
}
|
2015-11-27 08:24:36 -05:00
|
|
|
|
2016-09-11 19:17:19 +03:00
|
|
|
gpio_qmsi_set_power_state(port, DEVICE_PM_ACTIVE_STATE);
|
|
|
|
|
2015-11-27 08:24:36 -05:00
|
|
|
port->driver_api = &api_funcs;
|
2016-03-09 14:01:20 -03:00
|
|
|
return 0;
|
2015-11-27 08:24:36 -05:00
|
|
|
}
|