From 278da905bf669bf1464b8d3347460417ab37c0ec Mon Sep 17 00:00:00 2001 From: Francisco Munoz Date: Fri, 9 Aug 2019 16:28:05 -0700 Subject: [PATCH] drivers: ps2: Add driver for Microchip XEC family Add the PS2 driver for XEC Signed-off-by: Francisco Munoz --- drivers/ps2/CMakeLists.txt | 5 + drivers/ps2/Kconfig | 2 + drivers/ps2/Kconfig.xec | 26 ++++ drivers/ps2/ps2_mchp_xec.c | 259 +++++++++++++++++++++++++++++++++++++ 4 files changed, 292 insertions(+) create mode 100644 drivers/ps2/CMakeLists.txt create mode 100644 drivers/ps2/Kconfig.xec create mode 100644 drivers/ps2/ps2_mchp_xec.c diff --git a/drivers/ps2/CMakeLists.txt b/drivers/ps2/CMakeLists.txt new file mode 100644 index 00000000000..3ed9cc7219f --- /dev/null +++ b/drivers/ps2/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_PS2_XEC ps2_mchp_xec.c) diff --git a/drivers/ps2/Kconfig b/drivers/ps2/Kconfig index 6b119506d9b..c20c227ecfe 100644 --- a/drivers/ps2/Kconfig +++ b/drivers/ps2/Kconfig @@ -13,6 +13,8 @@ menuconfig PS2 if PS2 +source "drivers/ps2/Kconfig.xec" + module = PS2 module-str = ps2 source "subsys/logging/Kconfig.template.log_config" diff --git a/drivers/ps2/Kconfig.xec b/drivers/ps2/Kconfig.xec new file mode 100644 index 00000000000..67b2851ead6 --- /dev/null +++ b/drivers/ps2/Kconfig.xec @@ -0,0 +1,26 @@ +# Kconfig.xec - Microchip XEC PS2 configuration options +# +# Copyright (c) 2019 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +menuconfig PS2_XEC + bool "XEC Microchip PS2 driver" + depends on SOC_FAMILY_MEC + help + Enable the Microchip XEC PS2 IO driver. + +if PS2_XEC + +config PS2_XEC_0 + bool "PS2_XEC_0" + help + Enable PS2 0. + +config PS2_XEC_1 + bool "PS2_XEC_1" + help + Enable PS2 1. + +endif #PS2_XEC diff --git a/drivers/ps2/ps2_mchp_xec.c b/drivers/ps2/ps2_mchp_xec.c new file mode 100644 index 00000000000..58c4555e8be --- /dev/null +++ b/drivers/ps2/ps2_mchp_xec.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2019 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#define LOG_LEVEL CONFIG_PS2_LOG_LEVEL +LOG_MODULE_REGISTER(ps2_mchp_xec); + +/* in 50us units */ +#define PS2_TIMEOUT 10000 + +struct ps2_xec_config { + PS2_Type *base; + u8_t girq_id; + u8_t girq_bit; + u8_t isr_nvic; +}; + +struct ps2_xec_data { + ps2_callback_t callback_isr; + struct k_sem tx_lock; +}; + +static int ps2_xec_configure(struct device *dev, ps2_callback_t callback_isr) +{ + const struct ps2_xec_config *config = dev->config->config_info; + struct ps2_xec_data *data = dev->driver_data; + PS2_Type *base = config->base; + + u8_t __attribute__((unused)) dummy; + + if (!callback_isr) { + return -EINVAL; + } + + data->callback_isr = callback_isr; + + /* In case the self test for a PS2 device already finished and + * set the SOURCE bit to 1 we clear it before enabling the + * interrupts. Instances must be allocated before the BAT or + * the host may time out. + */ + MCHP_GIRQ_SRC(config->girq_id) = BIT(config->girq_bit); + dummy = base->TRX_BUFF; + base->STATUS = MCHP_PS2_STATUS_RW1C_MASK; + + /* Enable FSM and init instance in rx mode*/ + base->CTRL = MCHP_PS2_CTRL_EN_POS; + + /* We enable the interrupts in the EC aggregator so that the + * result can be forwarded to the ARM NVIC + */ + MCHP_GIRQ_ENSET(config->girq_id) = BIT(config->girq_bit); + + k_sem_give(&data->tx_lock); + + return 0; +} + + +static int ps2_xec_write(struct device *dev, u8_t value) +{ + const struct ps2_xec_config *config = dev->config->config_info; + struct ps2_xec_data *data = dev->driver_data; + PS2_Type *base = config->base; + int i = 0; + + u8_t __attribute__((unused)) dummy; + + if (k_sem_take(&data->tx_lock, K_NO_WAIT)) { + return -EACCES; + } + /* Allow the PS2 controller to complete a RX transaction. This + * is because the channel may be actively receiving data. + * In addition, it is necessary to wait for a previous TX + * transaction to complete. The PS2 block has a single + * FSM. + */ + while (((base->STATUS & + (MCHP_PS2_STATUS_RXD_RDY | MCHP_PS2_STATUS_TX_IDLE)) + != MCHP_PS2_STATUS_TX_IDLE) && (i < PS2_TIMEOUT)) { + k_busy_wait(50); + i++; + } + + if (unlikely(i == PS2_TIMEOUT)) { + LOG_DBG("PS2 write timed out"); + return -ETIMEDOUT; + } + + /* Inhibit ps2 controller and clear status register */ + base->CTRL = 0x00; + + /* Read to clear data ready bit in the status register*/ + dummy = base->TRX_BUFF; + + base->STATUS = MCHP_PS2_STATUS_RW1C_MASK; + /* Switch the interface to TX mode and enable state machine */ + base->CTRL = MCHP_PS2_CTRL_TR_TX | MCHP_PS2_CTRL_EN; + + /* Write value to TX/RX register */ + base->TRX_BUFF = value; + + k_sem_give(&data->tx_lock); + + return 0; +} + +static int ps2_xec_inhibit_interface(struct device *dev) +{ + const struct ps2_xec_config *config = dev->config->config_info; + struct ps2_xec_data *data = dev->driver_data; + PS2_Type *base = config->base; + + if (k_sem_take(&data->tx_lock, K_MSEC(10)) != 0) { + return -EACCES; + } + + base->CTRL = 0x00; + MCHP_GIRQ_SRC(config->girq_id) = BIT(config->girq_bit); + NVIC_ClearPendingIRQ(config->isr_nvic); + + k_sem_give(&data->tx_lock); + + return 0; +} + +static int ps2_xec_enable_interface(struct device *dev) +{ + const struct ps2_xec_config *config = dev->config->config_info; + struct ps2_xec_data *data = dev->driver_data; + PS2_Type *base = config->base; + + MCHP_GIRQ_SRC(config->girq_id) = BIT(config->girq_bit); + base->CTRL = MCHP_PS2_CTRL_EN; + + k_sem_give(&data->tx_lock); + + return 0; +} +static void ps2_xec_isr(void *arg) +{ + struct device *dev = (struct device *)arg; + const struct ps2_xec_config *config = dev->config->config_info; + struct ps2_xec_data *data = dev->driver_data; + PS2_Type *base = config->base; + u32_t status; + + MCHP_GIRQ_SRC(config->girq_id) = BIT(config->girq_bit); + + /* Read and clear status */ + status = base->STATUS; + + if (status & MCHP_PS2_STATUS_RXD_RDY) { + base->CTRL = 0x00; + if (data->callback_isr) { + data->callback_isr(dev, base->TRX_BUFF); + } + } else if (status & + (MCHP_PS2_STATUS_TX_TMOUT | MCHP_PS2_STATUS_TX_ST_TMOUT)) { + /* Clear sticky bits and go to read mode */ + base->STATUS = MCHP_PS2_STATUS_RW1C_MASK; + LOG_ERR("TX time out"); + } + + /* The control register reverts to RX automatically after + * transmiting the data + */ + base->CTRL = MCHP_PS2_CTRL_EN; +} + +static const struct ps2_driver_api ps2_xec_driver_api = { + .config = ps2_xec_configure, + .read = NULL, + .write = ps2_xec_write, + .disable_callback = ps2_xec_inhibit_interface, + .enable_callback = ps2_xec_enable_interface, +}; + +#ifdef CONFIG_PS2_XEC_0 +static int ps2_xec_init_0(struct device *dev); + +static const struct ps2_xec_config ps2_xec_config_0 = { + .base = (PS2_Type *) DT_PS2_XEC_0_BASE_ADDR, + .girq_id = MCHP_GIRQ18_ID, + .girq_bit = MCHP_PS2_0_GIRQ_POS, + .isr_nvic = DT_PS2_XEC_0_IRQ, +}; + +static struct ps2_xec_data ps2_xec_port_data_0; + +DEVICE_AND_API_INIT(ps2_xec_0, DT_PS2_XEC_0_LABEL, + &ps2_xec_init_0, + &ps2_xec_port_data_0, &ps2_xec_config_0, + POST_KERNEL, CONFIG_PS2_INIT_PRIORITY, + &ps2_xec_driver_api); + + +static int ps2_xec_init_0(struct device *dev) +{ + ARG_UNUSED(dev); + + struct ps2_xec_data *data = dev->driver_data; + + k_sem_init(&data->tx_lock, 0, 1); + + IRQ_CONNECT(DT_PS2_XEC_0_IRQ, + DT_PS2_XEC_0_IRQ_PRIORITY, + ps2_xec_isr, DEVICE_GET(ps2_xec_0), 0); + + irq_enable(DT_PS2_XEC_0_IRQ); + + return 0; +} +#endif /* CONFIG_PS2_XEC_0 */ + +#ifdef CONFIG_PS2_XEC_1 +static int ps2_xec_init_1(struct device *dev); + +static const struct ps2_xec_config ps2_xec_config_1 = { + .base = (PS2_Type *) DT_PS2_XEC_1_BASE_ADDR, + .girq_id = MCHP_GIRQ18_ID, + .girq_bit = MCHP_PS2_1_GIRQ_POS, + .isr_nvic = DT_PS2_XEC_1_IRQ, + +}; + +static struct ps2_xec_data ps2_xec_port_data_1; + +DEVICE_AND_API_INIT(ps2_xec_1, DT_PS2_XEC_1_LABEL, + &ps2_xec_init_1, + &ps2_xec_port_data_1, &ps2_xec_config_1, + POST_KERNEL, CONFIG_PS2_INIT_PRIORITY, + &ps2_xec_driver_api); + +static int ps2_xec_init_1(struct device *dev) +{ + ARG_UNUSED(dev); + + struct ps2_xec_data *data = dev->driver_data; + + k_sem_init(&data->tx_lock, 0, 1); + + IRQ_CONNECT(DT_PS2_XEC_1_IRQ, + DT_PS2_XEC_1_IRQ_PRIORITY, + ps2_xec_isr, DEVICE_GET(ps2_xec_1), 0); + + irq_enable(DT_PS2_XEC_1_IRQ); + + return 0; +} +#endif /* CONFIG_PS2_XEC_1 */