meta-digi-arm: linux-fslc: ccimx6ulsbc: Add IOEXP support
Signed-off-by: Alex Gonzalez <alex.gonzalez@digi.com>
This commit is contained in:
parent
4cf572edf5
commit
bcb942fc5b
|
|
@ -0,0 +1,892 @@
|
||||||
|
From f56dbb1e8b6a501e84d69ac5f18ec7a66cb4faa7 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alex Gonzalez <alex.gonzalez@digi.com>
|
||||||
|
Date: Mon, 23 Apr 2018 11:46:52 +0200
|
||||||
|
Subject: [PATCH] ccimx6ulsbcpro: Add IOEXP core I2C support
|
||||||
|
|
||||||
|
Synched with v4.14.78/master at:
|
||||||
|
3f8b03950b323db4ca89b1cdc1c2288f79facaa3
|
||||||
|
|
||||||
|
Signed-off-by: Alex Gonzalez <alex.gonzalez@digi.com>
|
||||||
|
---
|
||||||
|
arch/arm/configs/imx_v6_v7_defconfig | 1 +
|
||||||
|
drivers/mfd/Kconfig | 10 +
|
||||||
|
drivers/mfd/Makefile | 4 +-
|
||||||
|
drivers/mfd/mca-ioexp-core.c | 415 ++++++++++++++++++++++++++++++++
|
||||||
|
drivers/mfd/mca-ioexp-i2c.c | 186 ++++++++++++++
|
||||||
|
drivers/mfd/mca-ioexp-irq.c | 89 +++++++
|
||||||
|
include/linux/mfd/mca-ioexp/core.h | 87 +++++++
|
||||||
|
include/linux/mfd/mca-ioexp/registers.h | 15 ++
|
||||||
|
8 files changed, 805 insertions(+), 2 deletions(-)
|
||||||
|
create mode 100644 drivers/mfd/mca-ioexp-core.c
|
||||||
|
create mode 100644 drivers/mfd/mca-ioexp-i2c.c
|
||||||
|
create mode 100644 drivers/mfd/mca-ioexp-irq.c
|
||||||
|
create mode 100644 include/linux/mfd/mca-ioexp/core.h
|
||||||
|
create mode 100644 include/linux/mfd/mca-ioexp/registers.h
|
||||||
|
|
||||||
|
diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig
|
||||||
|
index ed31623082ac..a269983e3cba 100644
|
||||||
|
--- a/arch/arm/configs/imx_v6_v7_defconfig
|
||||||
|
+++ b/arch/arm/configs/imx_v6_v7_defconfig
|
||||||
|
@@ -242,6 +242,7 @@ CONFIG_MFD_DA9063=y
|
||||||
|
CONFIG_MFD_MC13XXX_SPI=y
|
||||||
|
CONFIG_MFD_MC13XXX_I2C=y
|
||||||
|
CONFIG_MFD_RN5T618=y
|
||||||
|
+CONFIG_MFD_MCA_IOEXP=y
|
||||||
|
CONFIG_MFD_STMPE=y
|
||||||
|
CONFIG_REGULATOR=y
|
||||||
|
CONFIG_REGULATOR_FIXED_VOLTAGE=y
|
||||||
|
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
|
||||||
|
index 42c72334d645..5b87d592aece 100644
|
||||||
|
--- a/drivers/mfd/Kconfig
|
||||||
|
+++ b/drivers/mfd/Kconfig
|
||||||
|
@@ -458,6 +458,16 @@ config MFD_MCA_CC8X
|
||||||
|
Additional drivers must be enabled in order to use the functionality
|
||||||
|
of the device (RTC, watchdog, ...).
|
||||||
|
|
||||||
|
+config MFD_MCA_IOEXP
|
||||||
|
+ bool "Digi IO Expander"
|
||||||
|
+ select MFD_CORE
|
||||||
|
+ select REGMAP_I2C
|
||||||
|
+ select REGMAP_IRQ
|
||||||
|
+ depends on I2C=y
|
||||||
|
+ help
|
||||||
|
+ Select this option to enable support for the Digi IO Expander.
|
||||||
|
+ This includes the GPIO and ADC drivers.
|
||||||
|
+
|
||||||
|
config MFD_MX25_TSADC
|
||||||
|
tristate "Freescale i.MX25 integrated Touchscreen and ADC unit"
|
||||||
|
select REGMAP_MMIO
|
||||||
|
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
|
||||||
|
index 28443313ba92..bfbaaba0d74f 100644
|
||||||
|
--- a/drivers/mfd/Makefile
|
||||||
|
+++ b/drivers/mfd/Makefile
|
||||||
|
@@ -249,5 +249,5 @@ obj-$(CONFIG_MFD_ROHM_BD718XX) += rohm-bd718x7.o
|
||||||
|
|
||||||
|
mca-cc6ul-objs := mca-cc6ul-core.o mca-cc6ul-irq.o mca-cc6ul-i2c.o
|
||||||
|
obj-$(CONFIG_MFD_MCA_CC6UL) += mca-cc6ul.o
|
||||||
|
-mca-cc8x-objs := mca-cc8x-core.o mca-cc8x-irq.o mca-cc8x-i2c.o
|
||||||
|
-obj-$(CONFIG_MFD_MCA_CC8X) += mca-cc8x.o
|
||||||
|
+mca-ioexp-objs := mca-ioexp-core.o mca-ioexp-irq.o mca-ioexp-i2c.o
|
||||||
|
+obj-$(CONFIG_MFD_MCA_IOEXP) += mca-ioexp.o
|
||||||
|
diff --git a/drivers/mfd/mca-ioexp-core.c b/drivers/mfd/mca-ioexp-core.c
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000000..19b3c7f2b4be
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/drivers/mfd/mca-ioexp-core.c
|
||||||
|
@@ -0,0 +1,415 @@
|
||||||
|
+/*
|
||||||
|
+ * Copyright 2017 - 2019 Digi International Inc
|
||||||
|
+ *
|
||||||
|
+ * This program is free software; you can redistribute it and/or modify it
|
||||||
|
+ * under the terms of the GNU General Public License as published by the
|
||||||
|
+ * Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
+ * option) any later version.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <linux/device.h>
|
||||||
|
+#include <linux/sysfs.h>
|
||||||
|
+#include <linux/delay.h>
|
||||||
|
+#include <linux/interrupt.h>
|
||||||
|
+#include <linux/mfd/core.h>
|
||||||
|
+#include <linux/slab.h>
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/regmap.h>
|
||||||
|
+#include <linux/suspend.h>
|
||||||
|
+#include <linux/proc_fs.h>
|
||||||
|
+#include <linux/kthread.h>
|
||||||
|
+#include <linux/uaccess.h>
|
||||||
|
+#include <linux/reboot.h>
|
||||||
|
+
|
||||||
|
+#include <linux/mfd/mca-common/core.h>
|
||||||
|
+#include <linux/mfd/mca-ioexp/core.h>
|
||||||
|
+
|
||||||
|
+#include <asm/unaligned.h>
|
||||||
|
+
|
||||||
|
+static struct resource mca_ioexp_gpios_resources[] = {
|
||||||
|
+ {
|
||||||
|
+ .name = MCA_IRQ_GPIO_BANK_0_NAME,
|
||||||
|
+ .start = MCA_IOEXP_IRQ_GPIO_BANK_0,
|
||||||
|
+ .end = MCA_IOEXP_IRQ_GPIO_BANK_0,
|
||||||
|
+ .flags = IORESOURCE_IRQ,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .name = MCA_IRQ_GPIO_BANK_1_NAME,
|
||||||
|
+ .start = MCA_IOEXP_IRQ_GPIO_BANK_1,
|
||||||
|
+ .end = MCA_IOEXP_IRQ_GPIO_BANK_1,
|
||||||
|
+ .flags = IORESOURCE_IRQ,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .name = MCA_IRQ_GPIO_BANK_2_NAME,
|
||||||
|
+ .start = MCA_IOEXP_IRQ_GPIO_BANK_2,
|
||||||
|
+ .end = MCA_IOEXP_IRQ_GPIO_BANK_2,
|
||||||
|
+ .flags = IORESOURCE_IRQ,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .name = MCA_IRQ_GPIO_BANK_3_NAME,
|
||||||
|
+ .start = MCA_IOEXP_IRQ_GPIO_BANK_3,
|
||||||
|
+ .end = MCA_IOEXP_IRQ_GPIO_BANK_3,
|
||||||
|
+ .flags = IORESOURCE_IRQ,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .name = MCA_IRQ_GPIO_BANK_4_NAME,
|
||||||
|
+ .start = MCA_IOEXP_IRQ_GPIO_BANK_4,
|
||||||
|
+ .end = MCA_IOEXP_IRQ_GPIO_BANK_4,
|
||||||
|
+ .flags = IORESOURCE_IRQ,
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .name = MCA_IRQ_GPIO_BANK_5_NAME,
|
||||||
|
+ .start = MCA_IOEXP_IRQ_GPIO_BANK_5,
|
||||||
|
+ .end = MCA_IOEXP_IRQ_GPIO_BANK_5,
|
||||||
|
+ .flags = IORESOURCE_IRQ,
|
||||||
|
+ },
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct mfd_cell mca_ioexp_devs[] = {
|
||||||
|
+ {
|
||||||
|
+ .name = MCA_IOEXP_DRVNAME_GPIO,
|
||||||
|
+ .num_resources = ARRAY_SIZE(mca_ioexp_gpios_resources),
|
||||||
|
+ .resources = mca_ioexp_gpios_resources,
|
||||||
|
+ .of_compatible = "digi,mca-ioexp-gpio",
|
||||||
|
+ },
|
||||||
|
+ {
|
||||||
|
+ .name = MCA_IOEXP_DRVNAME_ADC,
|
||||||
|
+ .of_compatible = "digi,mca-ioexp-adc",
|
||||||
|
+ },
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static ssize_t hwver_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
+ char *buf)
|
||||||
|
+{
|
||||||
|
+ struct mca_ioexp *ioexp = dev_get_drvdata(dev);
|
||||||
|
+
|
||||||
|
+ return sprintf(buf, "%d\n", ioexp->hw_version);
|
||||||
|
+}
|
||||||
|
+static DEVICE_ATTR(hw_version, S_IRUGO, hwver_show, NULL);
|
||||||
|
+
|
||||||
|
+static ssize_t fwver_show(struct device *dev, struct device_attribute *attr,
|
||||||
|
+ char *buf)
|
||||||
|
+{
|
||||||
|
+ struct mca_ioexp *ioexp = dev_get_drvdata(dev);
|
||||||
|
+
|
||||||
|
+ return sprintf(buf, "%d.%02d %s\n", MCA_FW_VER_MAJOR(ioexp->fw_version),
|
||||||
|
+ MCA_FW_VER_MINOR(ioexp->fw_version),
|
||||||
|
+ ioexp->fw_is_alpha ? "(alpha)" : "");
|
||||||
|
+}
|
||||||
|
+static DEVICE_ATTR(fw_version, S_IRUGO, fwver_show, NULL);
|
||||||
|
+
|
||||||
|
+static struct attribute *mca_ioexp_sysfs_entries[] = {
|
||||||
|
+ &dev_attr_hw_version.attr,
|
||||||
|
+ &dev_attr_fw_version.attr,
|
||||||
|
+ NULL,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static struct attribute_group mca_ioexp_attr_group = {
|
||||||
|
+ .name = NULL, /* put in device directory */
|
||||||
|
+ .attrs = mca_ioexp_sysfs_entries,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int read_reg_group(struct mca_ioexp *ioexp,
|
||||||
|
+ unsigned int start_addr,
|
||||||
|
+ unsigned int count,
|
||||||
|
+ uint8_t *dest)
|
||||||
|
+{
|
||||||
|
+ unsigned int i;
|
||||||
|
+ unsigned int error;
|
||||||
|
+
|
||||||
|
+ if (count == 0 || !dest)
|
||||||
|
+ return -1;
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < count; i++) {
|
||||||
|
+ const unsigned int reg_addr = start_addr + i;
|
||||||
|
+ unsigned int value;
|
||||||
|
+
|
||||||
|
+ error = regmap_read(ioexp->regmap, reg_addr, &value);
|
||||||
|
+ if (error) {
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Error reading register %02X (%d)\n",
|
||||||
|
+ reg_addr, error);
|
||||||
|
+ return -1;
|
||||||
|
+ }
|
||||||
|
+ dest[i] = value;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int mca_ioexp_suspend(struct device *dev)
|
||||||
|
+{
|
||||||
|
+ struct mca_ioexp *ioexp = dev_get_drvdata(dev);
|
||||||
|
+ int error;
|
||||||
|
+
|
||||||
|
+ if (!ioexp->preserved_regs)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+ error = read_reg_group(ioexp, MCA_GPIO_DIR_0,
|
||||||
|
+ ioexp->preserved_regs->gpio_dir.cnt,
|
||||||
|
+ ioexp->preserved_regs->gpio_dir.values);
|
||||||
|
+ if (error) {
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Failed to preserve MCA_GPIO_DIR registers.\n");
|
||||||
|
+ goto exit;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ error = read_reg_group(ioexp, MCA_GPIO_DATA_0,
|
||||||
|
+ ioexp->preserved_regs->gpio_data.cnt,
|
||||||
|
+ ioexp->preserved_regs->gpio_data.values);
|
||||||
|
+ if (error) {
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Failed to preserve MCA_GPIO_DATA registers.\n");
|
||||||
|
+ goto exit;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ error = read_reg_group(ioexp, MCA_GPIO_IRQ_CFG_0,
|
||||||
|
+ ioexp->preserved_regs->irq_cfg.cnt,
|
||||||
|
+ ioexp->preserved_regs->irq_cfg.values);
|
||||||
|
+ if (error) {
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Failed to preserve MCA_GPIO_IRQ_CFG registers.\n");
|
||||||
|
+ goto exit;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ error = read_reg_group(ioexp, MCA_IRQ_MASK_0,
|
||||||
|
+ ioexp->preserved_regs->irq_mask.cnt,
|
||||||
|
+ ioexp->preserved_regs->irq_mask.values);
|
||||||
|
+ if (error) {
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Failed to preserve MCA_IRQ_MASK registers.\n");
|
||||||
|
+ goto exit;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ error = read_reg_group(ioexp, MCA_REG_ADC_CFG0_0,
|
||||||
|
+ ioexp->preserved_regs->adc_cfg.cnt,
|
||||||
|
+ ioexp->preserved_regs->adc_cfg.values);
|
||||||
|
+ if (error) {
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Failed to preserve MCA_REG_ADC_CFG registers.\n");
|
||||||
|
+ goto exit;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ disable_irq(ioexp->chip_irq);
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+exit:
|
||||||
|
+ if (error)
|
||||||
|
+ dev_err(ioexp->dev, "Configuration will be lost on resume. The IOs in use might need to be reconfigured in order to work properly.\n");
|
||||||
|
+
|
||||||
|
+ disable_irq(ioexp->chip_irq);
|
||||||
|
+
|
||||||
|
+ /* Do not return errors or the device will not go to sleep. */
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int write_reg_group(struct mca_ioexp *ioexp,
|
||||||
|
+ unsigned int start_addr,
|
||||||
|
+ unsigned int count,
|
||||||
|
+ uint8_t *values)
|
||||||
|
+{
|
||||||
|
+ unsigned int i;
|
||||||
|
+ unsigned int error;
|
||||||
|
+
|
||||||
|
+ if (count == 0 || !values)
|
||||||
|
+ return -1;
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < count; i++) {
|
||||||
|
+ const unsigned int reg_addr = start_addr + i;
|
||||||
|
+ const unsigned int value = values[i];
|
||||||
|
+
|
||||||
|
+ error = regmap_write(ioexp->regmap, reg_addr, value);
|
||||||
|
+ if (error) {
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Error writing register %02X (%d)\n",
|
||||||
|
+ reg_addr, error);
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return error;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int mca_ioexp_resume(struct device *dev)
|
||||||
|
+{
|
||||||
|
+ struct mca_ioexp *ioexp = dev_get_drvdata(dev);
|
||||||
|
+ int error;
|
||||||
|
+
|
||||||
|
+ if (!ioexp->preserved_regs)
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ error = write_reg_group(ioexp, MCA_GPIO_DIR_0,
|
||||||
|
+ ioexp->preserved_regs->gpio_dir.cnt,
|
||||||
|
+ ioexp->preserved_regs->gpio_dir.values);
|
||||||
|
+ if (error)
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Failed to restore MCA_GPIO_DIR registers.\n");
|
||||||
|
+
|
||||||
|
+ error = write_reg_group(ioexp, MCA_GPIO_DATA_0,
|
||||||
|
+ ioexp->preserved_regs->gpio_data.cnt,
|
||||||
|
+ ioexp->preserved_regs->gpio_data.values);
|
||||||
|
+ if (error)
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Failed to restore MCA_GPIO_DATA registers.\n");
|
||||||
|
+
|
||||||
|
+ error = write_reg_group(ioexp, MCA_GPIO_IRQ_CFG_0,
|
||||||
|
+ ioexp->preserved_regs->irq_cfg.cnt,
|
||||||
|
+ ioexp->preserved_regs->irq_cfg.values);
|
||||||
|
+ if (error)
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Failed to restore MCA_GPIO_IRQ_CFG registers.\n");
|
||||||
|
+
|
||||||
|
+ error = write_reg_group(ioexp, MCA_IRQ_MASK_0,
|
||||||
|
+ ioexp->preserved_regs->irq_mask.cnt,
|
||||||
|
+ ioexp->preserved_regs->irq_mask.values);
|
||||||
|
+ if (error)
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Failed to restore MCA_IRQ_MASK registers.\n");
|
||||||
|
+
|
||||||
|
+ error = write_reg_group(ioexp, MCA_REG_ADC_CFG0_0,
|
||||||
|
+ ioexp->preserved_regs->adc_cfg.cnt,
|
||||||
|
+ ioexp->preserved_regs->adc_cfg.values);
|
||||||
|
+ if (error)
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Failed to restore MCA_REG_ADC_CFG registers.\n");
|
||||||
|
+
|
||||||
|
+ enable_irq(ioexp->chip_irq);
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+int mca_ioexp_device_init(struct mca_ioexp *ioexp, u32 irq)
|
||||||
|
+{
|
||||||
|
+ int ret;
|
||||||
|
+ unsigned int val;
|
||||||
|
+
|
||||||
|
+ ret = regmap_read(ioexp->regmap, MCA_DEVICE_ID, &val);
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Cannot read MCA IO Expander Device ID (%d)\n",
|
||||||
|
+ ret);
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
+ ioexp->dev_id = (u8)val;
|
||||||
|
+
|
||||||
|
+ if (ioexp->dev_id != MCA_IOEXP_DEVICE_ID_VAL) {
|
||||||
|
+ dev_err(ioexp->dev, "Invalid MCA IO Expander Device ID (%x)\n",
|
||||||
|
+ ioexp->dev_id);
|
||||||
|
+ return -ENODEV;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ret = regmap_read(ioexp->regmap, MCA_HW_VER, &val);
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_err(ioexp->dev, "Cannot read MCA Hardware Version (%d)\n",
|
||||||
|
+ ret);
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
+ ioexp->hw_version = (u8)val;
|
||||||
|
+
|
||||||
|
+ ret = regmap_bulk_read(ioexp->regmap, MCA_FW_VER_L, &val, 2);
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Cannot read MCA IO Expander Firmware Version (%d)\n",
|
||||||
|
+ ret);
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
+ ioexp->fw_version = (u16)(val & ~MCA_FW_VER_ALPHA_MASK);
|
||||||
|
+ ioexp->fw_is_alpha = val & MCA_FW_VER_ALPHA_MASK ? true : false;
|
||||||
|
+
|
||||||
|
+ ioexp->chip_irq = irq;
|
||||||
|
+ ioexp->gpio_base = -1;
|
||||||
|
+
|
||||||
|
+ if (of_find_property(ioexp->dev->of_node, "restore-config-on-resume",
|
||||||
|
+ NULL)) {
|
||||||
|
+ unsigned int gpio_num;
|
||||||
|
+
|
||||||
|
+ ioexp->preserved_regs = kzalloc(sizeof *ioexp->preserved_regs,
|
||||||
|
+ GFP_KERNEL | GFP_DMA);
|
||||||
|
+ if (!ioexp->preserved_regs) {
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Failed to allocate memory for preserved registers.\n");
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ret = regmap_read(ioexp->regmap, MCA_GPIO_NUM, &gpio_num);
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_err(ioexp->dev,
|
||||||
|
+ "Error reading MCA_GPIO_NUM (%d)\n", ret);
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ioexp->preserved_regs->gpio_dir.cnt = (gpio_num + 7) / 8;
|
||||||
|
+ ioexp->preserved_regs->gpio_dir.values =
|
||||||
|
+ kzalloc(ioexp->preserved_regs->gpio_dir.cnt,
|
||||||
|
+ GFP_KERNEL | GFP_DMA);
|
||||||
|
+ ioexp->preserved_regs->gpio_data.cnt = (gpio_num + 7) / 8;
|
||||||
|
+ ioexp->preserved_regs->gpio_data.values =
|
||||||
|
+ kzalloc(ioexp->preserved_regs->gpio_data.cnt,
|
||||||
|
+ GFP_KERNEL | GFP_DMA);
|
||||||
|
+ ioexp->preserved_regs->irq_cfg.cnt = gpio_num;
|
||||||
|
+ ioexp->preserved_regs->irq_cfg.values =
|
||||||
|
+ kzalloc(ioexp->preserved_regs->irq_cfg.cnt,
|
||||||
|
+ GFP_KERNEL | GFP_DMA);
|
||||||
|
+ ioexp->preserved_regs->irq_mask.cnt = 4;
|
||||||
|
+ ioexp->preserved_regs->irq_mask.values =
|
||||||
|
+ kzalloc(ioexp->preserved_regs->irq_mask.cnt,
|
||||||
|
+ GFP_KERNEL | GFP_DMA);
|
||||||
|
+ ioexp->preserved_regs->adc_cfg.cnt = gpio_num;
|
||||||
|
+ ioexp->preserved_regs->adc_cfg.values =
|
||||||
|
+ kzalloc(ioexp->preserved_regs->adc_cfg.cnt,
|
||||||
|
+ GFP_KERNEL | GFP_DMA);
|
||||||
|
+
|
||||||
|
+ } else {
|
||||||
|
+ ioexp->preserved_regs = NULL;
|
||||||
|
+ }
|
||||||
|
+ ret = mca_ioexp_irq_init(ioexp);
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_err(ioexp->dev, "Cannot initialize interrupts (%d)\n", ret);
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ret = mfd_add_devices(ioexp->dev, -1, mca_ioexp_devs,
|
||||||
|
+ ARRAY_SIZE(mca_ioexp_devs), NULL, ioexp->irq_base,
|
||||||
|
+ regmap_irq_get_domain(ioexp->regmap_irq));
|
||||||
|
+
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_err(ioexp->dev, "Cannot add MFD cells (%d)\n", ret);
|
||||||
|
+ goto out_irq;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ret = sysfs_create_group(&ioexp->dev->kobj, &mca_ioexp_attr_group);
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_err(ioexp->dev, "Cannot create sysfs entries (%d)\n", ret);
|
||||||
|
+ goto out_dev;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+out_dev:
|
||||||
|
+ mfd_remove_devices(ioexp->dev);
|
||||||
|
+out_irq:
|
||||||
|
+ mca_ioexp_irq_exit(ioexp);
|
||||||
|
+
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+void mca_ioexp_device_exit(struct mca_ioexp *ioexp)
|
||||||
|
+{
|
||||||
|
+ sysfs_remove_group(&ioexp->dev->kobj, &mca_ioexp_attr_group);
|
||||||
|
+ mfd_remove_devices(ioexp->dev);
|
||||||
|
+ mca_ioexp_irq_exit(ioexp);
|
||||||
|
+
|
||||||
|
+ if (!ioexp->preserved_regs) {
|
||||||
|
+ kfree(ioexp->preserved_regs->gpio_dir.values);
|
||||||
|
+ kfree(ioexp->preserved_regs->gpio_data.values);
|
||||||
|
+ kfree(ioexp->preserved_regs->irq_cfg.values);
|
||||||
|
+ kfree(ioexp->preserved_regs->irq_mask.values);
|
||||||
|
+ kfree(ioexp->preserved_regs->adc_cfg.values);
|
||||||
|
+ kfree(ioexp->preserved_regs);
|
||||||
|
+
|
||||||
|
+ ioexp->preserved_regs = NULL;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+MODULE_AUTHOR("Digi International Inc");
|
||||||
|
+MODULE_DESCRIPTION("MCA IO Expander driver");
|
||||||
|
+MODULE_LICENSE("GPL");
|
||||||
|
diff --git a/drivers/mfd/mca-ioexp-i2c.c b/drivers/mfd/mca-ioexp-i2c.c
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000000..29a5587ce683
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/drivers/mfd/mca-ioexp-i2c.c
|
||||||
|
@@ -0,0 +1,186 @@
|
||||||
|
+/*
|
||||||
|
+ * Copyright 2017 - 2019 Digi International Inc
|
||||||
|
+ *
|
||||||
|
+ * This program is free software; you can redistribute it and/or modify it
|
||||||
|
+ * under the terms of the GNU General Public License as published by the
|
||||||
|
+ * Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
+ * option) any later version.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <linux/kernel.h>
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/i2c.h>
|
||||||
|
+#include <linux/regmap.h>
|
||||||
|
+#include <linux/delay.h>
|
||||||
|
+#include <linux/slab.h>
|
||||||
|
+#include <linux/err.h>
|
||||||
|
+#include <linux/of.h>
|
||||||
|
+
|
||||||
|
+#include <linux/mfd/core.h>
|
||||||
|
+#include <linux/mfd/mca-common/core.h>
|
||||||
|
+#include <linux/mfd/mca-ioexp/core.h>
|
||||||
|
+
|
||||||
|
+static const struct regmap_range mca_ioexp_readable_ranges[] = {
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct regmap_range mca_ioexp_writeable_ranges[] = {
|
||||||
|
+ regmap_reg_range(MCA_IRQ_STATUS_0, MCA_IRQ_MASK_3),
|
||||||
|
+ regmap_reg_range(MCA_GPIO_DIR_0, MCA_GPIO_IRQ_CFG_63),
|
||||||
|
+ regmap_reg_range(MCA_REG_ADC_CFG0_0, MCA_REG_ADC_CFG0_31),
|
||||||
|
+ regmap_reg_range(MCA_REG_ADC_CFG1_0, MCA_REG_ADC_CFG1_31),
|
||||||
|
+ regmap_reg_range(MCA_REG_ADC_CFG2_0, MCA_REG_ADC_CFG2_31),
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct regmap_range mca_ioexp_volatile_ranges[] = {
|
||||||
|
+ /* Real volatile registers */
|
||||||
|
+ regmap_reg_range(MCA_IRQ_STATUS_0, MCA_IRQ_STATUS_3),
|
||||||
|
+ regmap_reg_range(MCA_GPIO_DATA_0, MCA_GPIO_DATA_7),
|
||||||
|
+ regmap_reg_range(MCA_GPIO_IRQ_STATUS_0, MCA_GPIO_IRQ_STATUS_7),
|
||||||
|
+ regmap_reg_range(MCA_REG_ADC_VAL_L_0, MCA_REG_ADC_VAL_H_31),
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Fake volatile registers.
|
||||||
|
+ *
|
||||||
|
+ * These registers could be cached but non-volatile registers makes
|
||||||
|
+ * regmap access each register one by one which has some drawbacks:
|
||||||
|
+ * - Breaks CRC in the protocol.
|
||||||
|
+ * - Requires the MCA firmware to process each access as a separate
|
||||||
|
+ * access, even when the data requested must be returned in bulk.
|
||||||
|
+ *
|
||||||
|
+ * For this reasons we will consider all registers volatile.
|
||||||
|
+ */
|
||||||
|
+ regmap_reg_range(MCA_DEVICE_ID, MCA_UID_9),
|
||||||
|
+ regmap_reg_range(MCA_IRQ_MASK_0, MCA_IRQ_MASK_3),
|
||||||
|
+ regmap_reg_range(MCA_GPIO_NUM, MCA_GPIO_DIR_7),
|
||||||
|
+ regmap_reg_range(MCA_GPIO_IRQ_CFG_0, MCA_GPIO_IRQ_CFG_63),
|
||||||
|
+ regmap_reg_range(MCA_REG_ADC_NUM_CH, MCA_REG_ADC_NUM_BYTES),
|
||||||
|
+ regmap_reg_range(MCA_REG_ADC_CFG0_0, MCA_REG_ADC_CFG0_31),
|
||||||
|
+ regmap_reg_range(MCA_REG_ADC_CFG1_0, MCA_REG_ADC_CFG1_31),
|
||||||
|
+ regmap_reg_range(MCA_REG_ADC_CFG2_0, MCA_REG_ADC_CFG2_31),
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct regmap_access_table mca_ioexp_readable_table = {
|
||||||
|
+ .yes_ranges = mca_ioexp_readable_ranges,
|
||||||
|
+ .n_yes_ranges = ARRAY_SIZE(mca_ioexp_readable_ranges),
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct regmap_access_table mca_ioexp_writeable_table = {
|
||||||
|
+ .yes_ranges = mca_ioexp_writeable_ranges,
|
||||||
|
+ .n_yes_ranges = ARRAY_SIZE(mca_ioexp_writeable_ranges),
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct regmap_access_table mca_ioexp_volatile_table = {
|
||||||
|
+ .yes_ranges = mca_ioexp_volatile_ranges,
|
||||||
|
+ .n_yes_ranges = ARRAY_SIZE(mca_ioexp_volatile_ranges),
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static struct regmap_config mca_ioexp_regmap_config = {
|
||||||
|
+ .reg_bits = 16,
|
||||||
|
+ .val_bits = 8,
|
||||||
|
+ .max_register = 0xFFFF,
|
||||||
|
+
|
||||||
|
+ .rd_table = &mca_ioexp_readable_table,
|
||||||
|
+ .wr_table = &mca_ioexp_writeable_table,
|
||||||
|
+ .volatile_table = &mca_ioexp_volatile_table,
|
||||||
|
+
|
||||||
|
+ .cache_type = REGCACHE_RBTREE,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct of_device_id mca_ioexp_dt_ids[] = {
|
||||||
|
+ { .compatible = "digi,mca_ioexp", },
|
||||||
|
+ { }
|
||||||
|
+};
|
||||||
|
+MODULE_DEVICE_TABLE(of, mca_ioexp_dt_ids);
|
||||||
|
+
|
||||||
|
+static int mca_ioexp_i2c_probe(struct i2c_client *i2c,
|
||||||
|
+ const struct i2c_device_id *id)
|
||||||
|
+{
|
||||||
|
+ struct mca_ioexp *ioexp;
|
||||||
|
+ int ret;
|
||||||
|
+
|
||||||
|
+ ioexp = devm_kzalloc(&i2c->dev, sizeof(struct mca_ioexp), GFP_KERNEL);
|
||||||
|
+ if (ioexp == NULL)
|
||||||
|
+ return -ENOMEM;
|
||||||
|
+
|
||||||
|
+ i2c_set_clientdata(i2c, ioexp);
|
||||||
|
+ ioexp->dev = &i2c->dev;
|
||||||
|
+ ioexp->chip_irq = i2c->irq;
|
||||||
|
+ ioexp->regmap = devm_regmap_init_i2c(i2c, &mca_ioexp_regmap_config);
|
||||||
|
+ if (IS_ERR(ioexp->regmap)) {
|
||||||
|
+ ret = PTR_ERR(ioexp->regmap);
|
||||||
|
+ dev_err(ioexp->dev, "Failed to allocate register map (%d)\n", ret);
|
||||||
|
+ goto err_regmap;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ret = mca_ioexp_device_init(ioexp, i2c->irq);
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_err(ioexp->dev, "Failed to init i2c device (%d)\n", ret);
|
||||||
|
+ goto err_regmap;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+err_regmap:
|
||||||
|
+ devm_kfree(ioexp->dev, ioexp);
|
||||||
|
+
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int mca_ioexp_i2c_remove(struct i2c_client *i2c)
|
||||||
|
+{
|
||||||
|
+ struct mca_ioexp *ioexp = i2c_get_clientdata(i2c);
|
||||||
|
+
|
||||||
|
+ mca_ioexp_device_exit(ioexp);
|
||||||
|
+ devm_kfree(ioexp->dev, ioexp);
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void mca_ioexp_i2c_shutdown(struct i2c_client *i2c)
|
||||||
|
+{
|
||||||
|
+ struct mca_ioexp *ioexp = i2c_get_clientdata(i2c);
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Disable the IRQ so that the I/O Expander does not wake-up the MCA
|
||||||
|
+ * when powered off.
|
||||||
|
+ */
|
||||||
|
+ disable_irq(ioexp->chip_irq);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+#ifdef CONFIG_PM
|
||||||
|
+static int mca_ioexp_i2c_suspend(struct device *dev)
|
||||||
|
+{
|
||||||
|
+ return mca_ioexp_suspend(dev);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static int mca_ioexp_i2c_resume(struct device *dev)
|
||||||
|
+{
|
||||||
|
+ return mca_ioexp_resume(dev);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static SIMPLE_DEV_PM_OPS(mca_ioexp_i2c_pm_ops,
|
||||||
|
+ mca_ioexp_i2c_suspend,
|
||||||
|
+ mca_ioexp_i2c_resume);
|
||||||
|
+#endif
|
||||||
|
+
|
||||||
|
+static const struct i2c_device_id mca_ioexp_i2c_id[] = {
|
||||||
|
+ {"mca_ioexp", 0},
|
||||||
|
+ {},
|
||||||
|
+};
|
||||||
|
+MODULE_DEVICE_TABLE(i2c, mca_ioexp_i2c_id);
|
||||||
|
+
|
||||||
|
+static struct i2c_driver mca_ioexp_i2c_driver = {
|
||||||
|
+ .driver = {
|
||||||
|
+ .name = "mca_ioexp",
|
||||||
|
+ .of_match_table = of_match_ptr(mca_ioexp_dt_ids),
|
||||||
|
+#ifdef CONFIG_PM
|
||||||
|
+ .pm = &mca_ioexp_i2c_pm_ops,
|
||||||
|
+#endif
|
||||||
|
+ },
|
||||||
|
+ .probe = mca_ioexp_i2c_probe,
|
||||||
|
+ .remove = mca_ioexp_i2c_remove,
|
||||||
|
+ .shutdown = mca_ioexp_i2c_shutdown,
|
||||||
|
+ .id_table = mca_ioexp_i2c_id,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+module_i2c_driver(mca_ioexp_i2c_driver);
|
||||||
|
diff --git a/drivers/mfd/mca-ioexp-irq.c b/drivers/mfd/mca-ioexp-irq.c
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000000..439149765dd0
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/drivers/mfd/mca-ioexp-irq.c
|
||||||
|
@@ -0,0 +1,89 @@
|
||||||
|
+/*
|
||||||
|
+ * Copyright 2017 - 2019 Digi International Inc
|
||||||
|
+ *
|
||||||
|
+ * This program is free software; you can redistribute it and/or modify it
|
||||||
|
+ * under the terms of the GNU General Public License as published by the
|
||||||
|
+ * Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
+ * option) any later version.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#include <linux/kernel.h>
|
||||||
|
+#include <linux/module.h>
|
||||||
|
+#include <linux/irq.h>
|
||||||
|
+#include <linux/mfd/core.h>
|
||||||
|
+#include <linux/interrupt.h>
|
||||||
|
+#include <linux/regmap.h>
|
||||||
|
+#include <linux/mfd/mca-ioexp/core.h>
|
||||||
|
+#include <linux/mfd/mca-common/core.h>
|
||||||
|
+
|
||||||
|
+#define MCA_IOEXP_IRQ_0_OFFSET 0
|
||||||
|
+#define MCA_IOEXP_IRQ_1_OFFSET 1
|
||||||
|
+#define MCA_IOEXP_IRQ_2_OFFSET 2
|
||||||
|
+#define MCA_IOEXP_IRQ_3_OFFSET 3
|
||||||
|
+
|
||||||
|
+static const struct regmap_irq mca_ioexp_irqs[] = {
|
||||||
|
+ [MCA_IOEXP_IRQ_GPIO_BANK_0] = {
|
||||||
|
+ .reg_offset = MCA_IOEXP_IRQ_1_OFFSET,
|
||||||
|
+ .mask = MCA_GPIO_BANK_0,
|
||||||
|
+ },
|
||||||
|
+ [MCA_IOEXP_IRQ_GPIO_BANK_1] = {
|
||||||
|
+ .reg_offset = MCA_IOEXP_IRQ_1_OFFSET,
|
||||||
|
+ .mask = MCA_GPIO_BANK_1,
|
||||||
|
+ },
|
||||||
|
+ [MCA_IOEXP_IRQ_GPIO_BANK_2] = {
|
||||||
|
+ .reg_offset = MCA_IOEXP_IRQ_1_OFFSET,
|
||||||
|
+ .mask = MCA_GPIO_BANK_2,
|
||||||
|
+ },
|
||||||
|
+ [MCA_IOEXP_IRQ_GPIO_BANK_3] = {
|
||||||
|
+ .reg_offset = MCA_IOEXP_IRQ_1_OFFSET,
|
||||||
|
+ .mask = MCA_GPIO_BANK_3,
|
||||||
|
+ },
|
||||||
|
+ [MCA_IOEXP_IRQ_GPIO_BANK_4] = {
|
||||||
|
+ .reg_offset = MCA_IOEXP_IRQ_1_OFFSET,
|
||||||
|
+ .mask = MCA_GPIO_BANK_4,
|
||||||
|
+ },
|
||||||
|
+ [MCA_IOEXP_IRQ_GPIO_BANK_5] = {
|
||||||
|
+ .reg_offset = MCA_IOEXP_IRQ_1_OFFSET,
|
||||||
|
+ .mask = MCA_GPIO_BANK_5,
|
||||||
|
+ },
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static const struct regmap_irq_chip mca_ioexp_irq_chip = {
|
||||||
|
+ .name = "mca-ioexp-irq",
|
||||||
|
+ .irqs = mca_ioexp_irqs,
|
||||||
|
+ .num_irqs = ARRAY_SIZE(mca_ioexp_irqs),
|
||||||
|
+ .num_regs = MCA_IOEXP_NUM_IRQ_REGS,
|
||||||
|
+ .status_base = MCA_IRQ_STATUS_0,
|
||||||
|
+ .mask_base = MCA_IRQ_MASK_0,
|
||||||
|
+ .ack_base = MCA_IRQ_STATUS_0,
|
||||||
|
+ .init_ack_masked = true,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+int mca_ioexp_irq_init(struct mca_ioexp *ioexp)
|
||||||
|
+{
|
||||||
|
+ int ret;
|
||||||
|
+ const int irq_flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT | IRQF_SHARED;
|
||||||
|
+
|
||||||
|
+ if (!ioexp->chip_irq) {
|
||||||
|
+ dev_err(ioexp->dev, "No IRQ configured\n");
|
||||||
|
+ return -EINVAL;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ioexp->irq_base = -1;
|
||||||
|
+ ret = regmap_add_irq_chip(ioexp->regmap, ioexp->chip_irq,
|
||||||
|
+ irq_flags,
|
||||||
|
+ ioexp->irq_base, &mca_ioexp_irq_chip,
|
||||||
|
+ &ioexp->regmap_irq);
|
||||||
|
+ if (ret) {
|
||||||
|
+ dev_err(ioexp->dev, "Failed to request IRQ %d (%d)\n",
|
||||||
|
+ ioexp->chip_irq, ret);
|
||||||
|
+ return ret;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return 0;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+void mca_ioexp_irq_exit(struct mca_ioexp *ioexp)
|
||||||
|
+{
|
||||||
|
+ regmap_del_irq_chip(ioexp->chip_irq, ioexp->regmap_irq);
|
||||||
|
+}
|
||||||
|
diff --git a/include/linux/mfd/mca-ioexp/core.h b/include/linux/mfd/mca-ioexp/core.h
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000000..cb9969262da5
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/linux/mfd/mca-ioexp/core.h
|
||||||
|
@@ -0,0 +1,87 @@
|
||||||
|
+/*
|
||||||
|
+ * Copyright 2017 - 2019 Digi International Inc
|
||||||
|
+ *
|
||||||
|
+ * This program is free software; you can redistribute it and/or modify it
|
||||||
|
+ * under the terms of the GNU General Public License as published by the
|
||||||
|
+ * Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
+ * option) any later version.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#ifndef MFD_MCA_IOEXP_CORE_H_
|
||||||
|
+#define MFD_MCA_IOEXP_CORE_H_
|
||||||
|
+
|
||||||
|
+#include <linux/interrupt.h>
|
||||||
|
+#include <linux/mfd/mca-ioexp/registers.h>
|
||||||
|
+
|
||||||
|
+#define MCA_IOEXP_DRVNAME_ADC "mca-ioexp-adc"
|
||||||
|
+#define MCA_IOEXP_DRVNAME_GPIO "mca-ioexp-gpio"
|
||||||
|
+
|
||||||
|
+#define MCA_IOEXP_DEVICE_ID_VAL 0x37
|
||||||
|
+#define MCA_IOEXP_ADDR_LEN 2
|
||||||
|
+#define MCA_IOEXP_MAX_FRAME_DATA_LEN 256
|
||||||
|
+
|
||||||
|
+/* Interrupts */
|
||||||
|
+enum mca_ioexp_irqs {
|
||||||
|
+ MCA_IOEXP_IRQ_GPIO_BANK_0,
|
||||||
|
+ MCA_IOEXP_IRQ_GPIO_BANK_1,
|
||||||
|
+ MCA_IOEXP_IRQ_GPIO_BANK_2,
|
||||||
|
+ MCA_IOEXP_IRQ_GPIO_BANK_3,
|
||||||
|
+ MCA_IOEXP_IRQ_GPIO_BANK_4,
|
||||||
|
+ MCA_IOEXP_IRQ_GPIO_BANK_5,
|
||||||
|
+ /* ... */
|
||||||
|
+
|
||||||
|
+ MCA_IOEXP_NUM_IRQS,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+/* Number of interrupt registers */
|
||||||
|
+#define MCA_IOEXP_NUM_IRQ_REGS 4
|
||||||
|
+
|
||||||
|
+struct mca_ioexp {
|
||||||
|
+ struct device *dev;
|
||||||
|
+ u8 dev_id;
|
||||||
|
+ u8 hw_version;
|
||||||
|
+ bool fw_is_alpha;
|
||||||
|
+ u16 fw_version;
|
||||||
|
+ u32 flags;
|
||||||
|
+ struct regmap *regmap;
|
||||||
|
+ struct regmap_irq_chip_data *regmap_irq;
|
||||||
|
+ int chip_irq;
|
||||||
|
+ u32 irq_base;
|
||||||
|
+ int gpio_base;
|
||||||
|
+
|
||||||
|
+ struct {
|
||||||
|
+ struct {
|
||||||
|
+ uint8_t *values;
|
||||||
|
+ uint8_t cnt;
|
||||||
|
+ } gpio_dir;
|
||||||
|
+
|
||||||
|
+ struct {
|
||||||
|
+ uint8_t *values;
|
||||||
|
+ uint8_t cnt;
|
||||||
|
+ } gpio_data;
|
||||||
|
+
|
||||||
|
+ struct {
|
||||||
|
+ uint8_t *values;
|
||||||
|
+ uint8_t cnt;
|
||||||
|
+ } irq_cfg;
|
||||||
|
+
|
||||||
|
+ struct {
|
||||||
|
+ uint8_t *values;
|
||||||
|
+ uint8_t cnt;
|
||||||
|
+ } irq_mask;
|
||||||
|
+
|
||||||
|
+ struct {
|
||||||
|
+ uint8_t *values;
|
||||||
|
+ uint8_t cnt;
|
||||||
|
+ } adc_cfg;
|
||||||
|
+ } *preserved_regs;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+int mca_ioexp_device_init(struct mca_ioexp *ioexp, u32 irq);
|
||||||
|
+int mca_ioexp_irq_init(struct mca_ioexp *ioexp);
|
||||||
|
+void mca_ioexp_device_exit(struct mca_ioexp *ioexp);
|
||||||
|
+void mca_ioexp_irq_exit(struct mca_ioexp *ioexp);
|
||||||
|
+int mca_ioexp_suspend(struct device *dev);
|
||||||
|
+int mca_ioexp_resume(struct device *dev);
|
||||||
|
+
|
||||||
|
+#endif /* MFD_MCA_IOEXP_CORE_H_ */
|
||||||
|
diff --git a/include/linux/mfd/mca-ioexp/registers.h b/include/linux/mfd/mca-ioexp/registers.h
|
||||||
|
new file mode 100644
|
||||||
|
index 000000000000..9359c9953e0d
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/include/linux/mfd/mca-ioexp/registers.h
|
||||||
|
@@ -0,0 +1,15 @@
|
||||||
|
+/*
|
||||||
|
+ * Copyright 2017 - 2019 Digi International Inc
|
||||||
|
+ *
|
||||||
|
+ * This program is free software; you can redistribute it and/or modify it
|
||||||
|
+ * under the terms of the GNU General Public License as published by the
|
||||||
|
+ * Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
+ * option) any later version.
|
||||||
|
+ */
|
||||||
|
+
|
||||||
|
+#ifndef MCA_IOEXP_REGISTERS_H_
|
||||||
|
+#define MCA_IOEXP_REGISTERS_H_
|
||||||
|
+
|
||||||
|
+#include <linux/bitops.h>
|
||||||
|
+
|
||||||
|
+#endif /* MCA_IOEXP_REGISTERS_H_ */
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
From 4a2907834799eda9bd681ccd85557d6433666903 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alex Gonzalez <alex.gonzalez@digi.com>
|
||||||
|
Date: Mon, 23 Apr 2018 11:47:38 +0200
|
||||||
|
Subject: [PATCH] ccimx6ulsbcpro: Add IOEXP GPIO support
|
||||||
|
|
||||||
|
Synched with v4.14.78/master at:
|
||||||
|
3f8b03950b323db4ca89b1cdc1c2288f79facaa3
|
||||||
|
|
||||||
|
Signed-off-by: Alex Gonzalez <alex.gonzalez@digi.com>
|
||||||
|
---
|
||||||
|
arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts | 23 +++++++++++++++++++++++
|
||||||
|
1 file changed, 23 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts
|
||||||
|
index afc87b34f441..a818aec68959 100644
|
||||||
|
--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts
|
||||||
|
+++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts
|
||||||
|
@@ -130,6 +130,29 @@
|
||||||
|
irq-gpios = <&gpio5 2 GPIO_ACTIVE_HIGH>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
+
|
||||||
|
+ mca_ioexp: mca_io@6e {
|
||||||
|
+ compatible = "digi,mca_ioexp";
|
||||||
|
+ reg = <0x6e>;
|
||||||
|
+ interrupt-parent = <&mca_gpio>;
|
||||||
|
+ interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
|
||||||
|
+ restore-config-on-resume;
|
||||||
|
+ status = "okay";
|
||||||
|
+
|
||||||
|
+ interrupt-controller;
|
||||||
|
+ #interrupt-cells = <2>;
|
||||||
|
+ pinctrl-names = "default";
|
||||||
|
+
|
||||||
|
+ mca_ioexp_gpio: gpio {
|
||||||
|
+ compatible = "digi,mca-ioexp-gpio";
|
||||||
|
+ gpio-controller;
|
||||||
|
+ #gpio-cells = <2>;
|
||||||
|
+
|
||||||
|
+ interrupt-parent = <&mca_ioexp>;
|
||||||
|
+ interrupt-controller;
|
||||||
|
+ #interrupt-cells = <2>;
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
};
|
||||||
|
|
||||||
|
&lcdif {
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
From d39028a304c0059ee16c65462e3e653670482aca Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alex Gonzalez <alex.gonzalez@digi.com>
|
||||||
|
Date: Mon, 23 Apr 2018 11:48:15 +0200
|
||||||
|
Subject: [PATCH] ccimx6ulsbcpro: Add IOEXP ADC support
|
||||||
|
|
||||||
|
Synched with v4.14.78/master at:
|
||||||
|
3f8b03950b323db4ca89b1cdc1c2288f79facaa3
|
||||||
|
|
||||||
|
Signed-off-by: Alex Gonzalez <alex.gonzalez@digi.com>
|
||||||
|
---
|
||||||
|
arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts | 7 ++++++-
|
||||||
|
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts
|
||||||
|
index 7df31112cabf..b2b6a49391f4 100644
|
||||||
|
--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts
|
||||||
|
+++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts
|
||||||
|
@@ -153,6 +153,12 @@
|
||||||
|
interrupt-controller;
|
||||||
|
#interrupt-cells = <2>;
|
||||||
|
};
|
||||||
|
+
|
||||||
|
+ mca_ioexp_adc: adc {
|
||||||
|
+ compatible = "digi,mca-ioexp-adc";
|
||||||
|
+ digi,adc-ch-list = <3 4 5>;
|
||||||
|
+ digi,adc-vref = <3300000>;
|
||||||
|
+ };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -482,4 +488,3 @@
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
-
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
From ac65a7be2a14e2c5f90319674527257a53e59402 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alex Gonzalez <alex.gonzalez@digi.com>
|
||||||
|
Date: Tue, 8 Jan 2019 16:42:40 +0100
|
||||||
|
Subject: [PATCH] ARM: dts: ccimx6ulsbcpro: Configure touch GPIO reset line
|
||||||
|
|
||||||
|
The Goodix touch requires both the INT and reset lines in order to fix
|
||||||
|
an I2C address at startup.
|
||||||
|
|
||||||
|
This commit uses the IOEXP GPIO29 to control the DISP_5V supply which
|
||||||
|
controls the voltage source for the reset line pull-up. It also sets the
|
||||||
|
internal pull-up on the INT line so the line is not left floating.
|
||||||
|
|
||||||
|
Signed-off-by: Alex Gonzalez <alex.gonzalez@digi.com>
|
||||||
|
---
|
||||||
|
arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts | 3 ++-
|
||||||
|
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts
|
||||||
|
index 14274a0d6435..62133ede2736 100644
|
||||||
|
--- a/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts
|
||||||
|
+++ b/arch/arm/boot/dts/imx6ul-ccimx6ulsbcpro.dts
|
||||||
|
@@ -128,6 +128,7 @@
|
||||||
|
interrupt-parent = <&gpio5>;
|
||||||
|
interrupts = <2 IRQ_TYPE_EDGE_RISING>;
|
||||||
|
irq-gpios = <&gpio5 2 GPIO_ACTIVE_HIGH>;
|
||||||
|
+ reset-gpios = <&mca_ioexp_gpio 29 GPIO_ACTIVE_HIGH>;
|
||||||
|
status = "okay";
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -366,7 +367,7 @@
|
||||||
|
|
||||||
|
pinctrl_goodix_touch: goodixgrp{
|
||||||
|
fsl,pins = <
|
||||||
|
- MX6UL_PAD_SNVS_TAMPER2__GPIO5_IO02 0x1020
|
||||||
|
+ MX6UL_PAD_SNVS_TAMPER2__GPIO5_IO02 0x1b0b0
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
@ -13,3 +13,10 @@ SRC_URI_append_ccimx6ul_use-mainline-bsp = " \
|
||||||
file://0010-imx6ul-Add-RTC-MCA-support-for-ConnectCore-6UL-SOM.patch \
|
file://0010-imx6ul-Add-RTC-MCA-support-for-ConnectCore-6UL-SOM.patch \
|
||||||
file://0011-imx6ul-Add-MCA-power-key-support-for-ConnectCore-6UL.patch \
|
file://0011-imx6ul-Add-MCA-power-key-support-for-ConnectCore-6UL.patch \
|
||||||
"
|
"
|
||||||
|
|
||||||
|
SRC_URI_append_ccimx6ulsbc_use-mainline-bsp = " \
|
||||||
|
file://0001-ccimx6ulsbcpro-Add-IOEXP-core-I2C-support.patch \
|
||||||
|
file://0002-ccimx6ulsbcpro-Add-IOEXP-GPIO-support.patch \
|
||||||
|
file://0003-ccimx6ulsbcpro-Add-IOEXP-ADC-support.patch \
|
||||||
|
file://0004-ARM-dts-ccimx6ulsbcpro-Configure-touch-GPIO-reset-li.patch \
|
||||||
|
"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue