diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/gpio/gpio-eic.c | |
Diffstat (limited to 'drivers/gpio/gpio-eic.c')
| -rwxr-xr-x | drivers/gpio/gpio-eic.c | 864 |
1 files changed, 864 insertions, 0 deletions
diff --git a/drivers/gpio/gpio-eic.c b/drivers/gpio/gpio-eic.c new file mode 100755 index 00000000..8c34d496 --- /dev/null +++ b/drivers/gpio/gpio-eic.c @@ -0,0 +1,864 @@ +/* + * Copyright (C) 2012 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/gpio.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/bug.h> +#include <linux/of_device.h> +#include <linux/irqdomain.h> + +#include <soc/sprd/sci.h> +#include <soc/sprd/sci_glb_regs.h> +#include <soc/sprd/gpio.h> +#include <soc/sprd/adi.h> + +/* 16 GPIO share a group of registers */ +#define GPIO_GROUP_NR (16) +#define GPIO_GROUP_MASK (0xFFFF) + +#define GPIO_GROUP_OFFSET (0x80) +#define ANA_GPIO_GROUP_OFFSET (0x40) + +/* registers definitions for GPIO controller */ +#define REG_GPIO_DATA (0x0000) +#define REG_GPIO_DMSK (0x0004) +#define REG_GPIO_DIR (0x0008) /* only for gpio */ +#define REG_GPIO_IS (0x000c) /* only for gpio */ +#define REG_GPIO_IBE (0x0010) /* only for gpio */ +#define REG_GPIO_IEV (0x0014) +#define REG_GPIO_IE (0x0018) +#define REG_GPIO_RIS (0x001c) +#define REG_GPIO_MIS (0x0020) +#define REG_GPIO_IC (0x0024) +#define REG_GPIO_INEN (0x0028) /* only for gpio */ + +/* 8 EIC share a group of registers */ +#define EIC_GROUP_NR (8) +#define EIC_GROUP_MASK (0xFF) + +/* registers definitions for EIC controller */ +#define REG_EIC_DATA REG_GPIO_DATA +#define REG_EIC_DMSK REG_GPIO_DMSK +#define REG_EIC_IEV REG_GPIO_IEV +#define REG_EIC_IE REG_GPIO_IE +#define REG_EIC_RIS REG_GPIO_RIS +#define REG_EIC_MIS REG_GPIO_MIS +#define REG_EIC_IC REG_GPIO_IC +#define REG_EIC_TRIG (0x0028) /* only for eic */ +#define REG_EIC_0CTRL (0x0040) +#define REG_EIC_1CTRL (0x0044) +#define REG_EIC_2CTRL (0x0048) +#define REG_EIC_3CTRL (0x004c) +#define REG_EIC_4CTRL (0x0050) +#define REG_EIC_5CTRL (0x0054) +#define REG_EIC_6CTRL (0x0058) +#define REG_EIC_7CTRL (0x005c) +#define REG_EIC_DUMMYCTRL (0x0000) + +/* bits definitions for register REG_EIC_DUMMYCTRL */ +#define BIT_FORCE_CLK_DBNC BIT(15) +#define BIT_EIC_DBNC_EN BIT(14) +#define SHIFT_EIC_DBNC_CNT (0) +#define MASK_EIC_DBNC_CNT (0xFFF) +#define BITS_EIC_DBNC_CNT(_x_) ((_x) & 0xFFF) + +struct sci_gpio_chip { + struct gpio_chip chip; + + unsigned long base_addr; + uint32_t group_offset; + struct irq_domain *irq_domain; + int is_adi_gpio; + + uint32_t(*read_reg) (unsigned long addr); + void (*write_reg) (uint32_t value, unsigned long addr); + void (*set_bits) (uint32_t bits, unsigned long addr); + void (*clr_bits) (uint32_t bits, unsigned long addr); +}; + +#define to_sci_gpio(c) container_of(c, struct sci_gpio_chip, chip) + +/* D-Die regs ops */ +static uint32_t d_read_reg(unsigned long addr) +{ + return __raw_readl((const volatile void *)addr); +} + +static void d_write_reg(uint32_t value, unsigned long addr) +{ + __raw_writel(value, (volatile void *)addr); +} + +static void d_set_bits(uint32_t bits, unsigned long addr) +{ + __raw_writel(__raw_readl((const volatile void *)addr) | bits, (volatile void *)addr); +} + +static void d_clr_bits(uint32_t bits, unsigned long addr) +{ + __raw_writel(__raw_readl((const volatile void *)addr) & ~bits, (volatile void *)addr); +} + +/* A-Die regs ops */ +static uint32_t a_read_reg(unsigned long addr) +{ + return sci_adi_read(addr); +} + +static void a_write_reg(uint32_t value, unsigned long addr) +{ + sci_adi_raw_write(addr, value); +} + +static void a_set_bits(uint32_t bits, unsigned long addr) +{ + sci_adi_set(addr, bits); +} + +static void a_clr_bits(uint32_t bits, unsigned long addr) +{ + sci_adi_clr(addr, bits); +} + +static int sci_gpio_read(struct gpio_chip *chip, uint32_t offset, uint32_t reg) +{ + struct sci_gpio_chip *sci_gpio = to_sci_gpio(chip); + int group = offset / GPIO_GROUP_NR; + int bitof = offset & (GPIO_GROUP_NR - 1); + unsigned long addr = sci_gpio->base_addr + sci_gpio->group_offset * group + reg; + int value = sci_gpio->read_reg(addr) & GPIO_GROUP_MASK; + + return (value >> bitof) & 0x1; +} + +static void sci_gpio_write(struct gpio_chip *chip, uint32_t offset, + uint32_t reg, int value) +{ + struct sci_gpio_chip *sci_gpio = to_sci_gpio(chip); + int group = offset / GPIO_GROUP_NR; + int bitof = offset & (GPIO_GROUP_NR - 1); + unsigned long addr = sci_gpio->base_addr + sci_gpio->group_offset * group + reg; + + if (value) { + sci_gpio->set_bits(1 << bitof, addr); + } else { + sci_gpio->clr_bits(1 << bitof, addr); + } +} + +/* GPIO/EIC libgpio interfaces */ +static int sci_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + pr_debug("%s %d+%d\n", __FUNCTION__, chip->base, offset); + sci_gpio_write(chip, offset, REG_GPIO_DMSK, 1); + return 0; +} + +static void sci_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + pr_debug("%s %d+%d\n", __FUNCTION__, chip->base, offset); + sci_gpio_write(chip, offset, REG_GPIO_DMSK, 0); +} + +static int sci_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +{ + pr_debug("%s %d+%d\n", __FUNCTION__, chip->base, offset); + sci_gpio_write(chip, offset, REG_GPIO_DIR, 0); + sci_gpio_write(chip, offset, REG_GPIO_INEN, 1); + return 0; +} + +static int sci_eic_direction_input(struct gpio_chip *chip, unsigned offset) +{ + /* do nothing */ + pr_debug("%s %d+%d\n", __FUNCTION__, chip->base, offset); + return 0; +} + +static int sci_gpio_direction_output(struct gpio_chip *chip, unsigned offset, + int value) +{ + pr_debug("%s %d+%d %d\n", __FUNCTION__, chip->base, offset, value); + sci_gpio_write(chip, offset, REG_GPIO_DIR, 1); + sci_gpio_write(chip, offset, REG_GPIO_INEN, 0); + sci_gpio_write(chip, offset, REG_GPIO_DATA, value); + return 0; +} + +static int sci_gpio_get(struct gpio_chip *chip, unsigned offset) +{ + pr_debug("%s %d+%d\n", __FUNCTION__, chip->base, offset); + return sci_gpio_read(chip, offset, REG_GPIO_DATA); +} + +static void sci_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +{ + pr_debug("%s %d+%d %d\n", __FUNCTION__, chip->base, offset, value); + sci_gpio_write(chip, offset, REG_GPIO_DATA, value); +} + +static int sci_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, + unsigned debounce) +{ + /* not supported */ + pr_err("%s %d+%d\n", __FUNCTION__, chip->base, offset); + return -EINVAL; +} + +static int sci_eic_set_debounce(struct gpio_chip *chip, unsigned offset, + unsigned debounce) +{ + /* TODO */ + pr_info("%s %d+%d\n", __FUNCTION__, chip->base, offset); + return 0; +} + +#ifdef CONFIG_OF +static int sci_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct irq_domain *irq_domain; + struct sci_gpio_chip *sci_gpio = to_sci_gpio(chip); + + irq_domain = sci_gpio->irq_domain; + return irq_find_mapping(irq_domain, offset); +} + +static int sci_irq_to_gpio(struct gpio_chip *chip, unsigned irq) +{ + int base_irq; + struct irq_domain *irq_domain; + struct sci_gpio_chip *sci_gpio = to_sci_gpio(chip); + + irq_domain = sci_gpio->irq_domain; + base_irq = irq_find_mapping(irq_domain, 0); + return irq - base_irq; +} +#else +static int sci_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ + return chip->base + offset + GPIO_IRQ_START; +} + +static int sci_irq_to_gpio(struct gpio_chip *chip, unsigned irq) +{ + return irq - GPIO_IRQ_START - chip->base; +} + +#endif + +static struct sci_gpio_chip d_sci_gpio = { + .chip.label = "sprd-d-gpio", + .chip.request = sci_gpio_request, + .chip.free = sci_gpio_free, + .chip.direction_input = sci_gpio_direction_input, + .chip.get = sci_gpio_get, + .chip.direction_output = sci_gpio_direction_output, + .chip.set = sci_gpio_set, + .chip.set_debounce = sci_gpio_set_debounce, + .chip.to_irq = sci_gpio_to_irq, + + .group_offset = GPIO_GROUP_OFFSET, + .read_reg = d_read_reg, + .write_reg = d_write_reg, + .set_bits = d_set_bits, + .clr_bits = d_clr_bits, + .is_adi_gpio = 0, +}; + +static struct sci_gpio_chip a_sci_gpio = { + .chip.label = "sprd-a-gpio", + .chip.request = sci_gpio_request, + .chip.free = sci_gpio_free, + .chip.direction_input = sci_gpio_direction_input, + .chip.get = sci_gpio_get, + .chip.direction_output = sci_gpio_direction_output, + .chip.set = sci_gpio_set, + .chip.set_debounce = sci_gpio_set_debounce, + .chip.to_irq = sci_gpio_to_irq, + + .group_offset = ANA_GPIO_GROUP_OFFSET, + .read_reg = a_read_reg, + .write_reg = a_write_reg, + .set_bits = a_set_bits, + .clr_bits = a_clr_bits, + .is_adi_gpio = 1, +}; + +/* + * EIC has the same register layout with GPIO when it's used as GPI. + * So most implementation of GPIO can be shared by EIC. + */ +static struct sci_gpio_chip d_sci_eic = { + .chip.label = "sprd-d-eic", + .chip.request = sci_gpio_request, + .chip.free = sci_gpio_free, + .chip.direction_input = sci_eic_direction_input, + .chip.get = sci_gpio_get, + .chip.direction_output = NULL, + .chip.set = NULL, + .chip.set_debounce = sci_eic_set_debounce, + .chip.to_irq = sci_gpio_to_irq, + + .group_offset = GPIO_GROUP_OFFSET, + .read_reg = d_read_reg, + .write_reg = d_write_reg, + .set_bits = d_set_bits, + .clr_bits = d_clr_bits, + .is_adi_gpio = 0, +}; + +static struct sci_gpio_chip a_sci_eic = { + .chip.label = "sprd-a-eic", + .chip.request = sci_gpio_request, + .chip.free = sci_gpio_free, + .chip.direction_input = sci_eic_direction_input, + .chip.get = sci_gpio_get, + .chip.direction_output = NULL, + .chip.set = NULL, + .chip.set_debounce = sci_eic_set_debounce, + .chip.to_irq = sci_gpio_to_irq, + + .group_offset = ANA_GPIO_GROUP_OFFSET, + .read_reg = a_read_reg, + .write_reg = a_write_reg, + .set_bits = a_set_bits, + .clr_bits = a_clr_bits, + .is_adi_gpio = 1, +}; + +/* GPIO/EIC irq interfaces */ +static void sci_gpio_irq_ack(struct irq_data *data) +{ + struct gpio_chip *chip = (struct gpio_chip *)data->chip_data; + int offset = sci_irq_to_gpio(chip, data->irq); + pr_debug("%s %d+%d\n", __FUNCTION__, chip->base, offset); + sci_gpio_write(chip, offset, REG_GPIO_IC, 1); +} + +static void sci_gpio_irq_mask(struct irq_data *data) +{ + struct gpio_chip *chip = (struct gpio_chip *)data->chip_data; + int offset = sci_irq_to_gpio(chip, data->irq); + pr_debug("%s %d+%d\n", __FUNCTION__, chip->base, offset); + sci_gpio_write(chip, offset, REG_GPIO_IE, 0); +} + +static void sci_gpio_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = (struct gpio_chip *)data->chip_data; + int offset = sci_irq_to_gpio(chip, data->irq); + pr_debug("%s %d+%d\n", __FUNCTION__, chip->base, offset); + sci_gpio_write(chip, offset, REG_GPIO_IE, 1); +} + +static void sci_eic_irq_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = (struct gpio_chip *)data->chip_data; + int offset = sci_irq_to_gpio(chip, data->irq); + pr_debug("%s %d+%d\n", __FUNCTION__, chip->base, offset); + sci_gpio_write(chip, offset, REG_EIC_IE, 1); + + /* TODO: the interval of two EIC trigger needs be longer than 2ms */ + sci_gpio_write(chip, offset, REG_EIC_TRIG, 1); +} + +static int sci_gpio_irq_set_type(struct irq_data *data, unsigned int flow_type) +{ + struct gpio_chip *chip = (struct gpio_chip *)data->chip_data; + int offset = sci_irq_to_gpio(chip, data->irq); + pr_debug("%s %d+%d %d\n", __FUNCTION__, chip->base, offset, flow_type); + switch (flow_type) { + case IRQ_TYPE_EDGE_RISING: + sci_gpio_write(chip, offset, REG_GPIO_IS, 0); + sci_gpio_write(chip, offset, REG_GPIO_IBE, 0); + sci_gpio_write(chip, offset, REG_GPIO_IEV, 1); + __irq_set_handler_locked(data->irq, handle_edge_irq); + break; + case IRQ_TYPE_EDGE_FALLING: + sci_gpio_write(chip, offset, REG_GPIO_IS, 0); + sci_gpio_write(chip, offset, REG_GPIO_IBE, 0); + sci_gpio_write(chip, offset, REG_GPIO_IEV, 0); + __irq_set_handler_locked(data->irq, handle_edge_irq); + break; + case IRQ_TYPE_EDGE_BOTH: + sci_gpio_write(chip, offset, REG_GPIO_IS, 0); + sci_gpio_write(chip, offset, REG_GPIO_IBE, 1); + __irq_set_handler_locked(data->irq, handle_edge_irq); + break; + case IRQ_TYPE_LEVEL_HIGH: + sci_gpio_write(chip, offset, REG_GPIO_IS, 1); + sci_gpio_write(chip, offset, REG_GPIO_IBE, 0); + sci_gpio_write(chip, offset, REG_GPIO_IEV, 1); + __irq_set_handler_locked(data->irq, handle_level_irq); + break; + case IRQ_TYPE_LEVEL_LOW: + sci_gpio_write(chip, offset, REG_GPIO_IS, 1); + sci_gpio_write(chip, offset, REG_GPIO_IBE, 0); + sci_gpio_write(chip, offset, REG_GPIO_IEV, 0); + __irq_set_handler_locked(data->irq, handle_level_irq); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int sci_eic_irq_set_type(struct irq_data *data, unsigned int flow_type) +{ + struct gpio_chip *chip = (struct gpio_chip *)data->chip_data; + int offset = sci_irq_to_gpio(chip, data->irq); + pr_debug("%s %d+%d %d\n", __FUNCTION__, chip->base, offset, flow_type); + switch (flow_type) { + case IRQ_TYPE_LEVEL_HIGH: + sci_gpio_write(chip, offset, REG_EIC_IEV, 1); + __irq_set_handler_locked(data->irq, handle_level_irq); + break; + case IRQ_TYPE_LEVEL_LOW: + sci_gpio_write(chip, offset, REG_EIC_IEV, 0); + __irq_set_handler_locked(data->irq, handle_level_irq); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int sci_gpio_irq_set_wake(struct irq_data *data, unsigned int on) +{ + return on ? 0 : -EPERM; +} + +static struct irq_chip d_gpio_irq_chip = { + .name = "irq-d-gpio", + .irq_disable = sci_gpio_irq_mask, + .irq_ack = sci_gpio_irq_ack, + .irq_mask = sci_gpio_irq_mask, + .irq_unmask = sci_gpio_irq_unmask, + .irq_set_type = sci_gpio_irq_set_type, + .irq_set_wake = sci_gpio_irq_set_wake, +}; + +static struct irq_chip a_gpio_irq_chip = { + .name = "irq-a-gpio", + .irq_disable = sci_gpio_irq_mask, + .irq_ack = sci_gpio_irq_ack, + .irq_mask = sci_gpio_irq_mask, + .irq_unmask = sci_gpio_irq_unmask, + .irq_set_type = sci_gpio_irq_set_type, + .irq_set_wake = sci_gpio_irq_set_wake, +}; + +/* + * EIC has the same register layout with GPIO when it's used as GPI. + * So most implementation of GPIO can be shared by EIC. + */ +static struct irq_chip d_eic_irq_chip = { + .name = "irq-d-eic", + .irq_disable = sci_gpio_irq_mask, + .irq_ack = sci_gpio_irq_ack, + .irq_mask = sci_gpio_irq_mask, + .irq_unmask = sci_eic_irq_unmask, + .irq_set_type = sci_eic_irq_set_type, +}; + +static struct irq_chip a_eic_irq_chip = { + .name = "irq-a-eic", + .irq_disable = sci_gpio_irq_mask, + .irq_ack = sci_gpio_irq_ack, + .irq_mask = sci_gpio_irq_mask, + .irq_unmask = sci_eic_irq_unmask, + .irq_set_type = sci_eic_irq_set_type, +}; + +static void gpio_eic_handler(int irq, struct gpio_chip *chip) +{ + struct sci_gpio_chip *sci_gpio = to_sci_gpio(chip); + int group, n, value, count = 0; + unsigned long addr; + + pr_debug("%s %d+%d %d\n", __FUNCTION__, chip->base, chip->ngpio, irq); + for (group = 0; group * GPIO_GROUP_NR < chip->ngpio; group++) { + addr = + sci_gpio->base_addr + sci_gpio->group_offset * group + + REG_GPIO_MIS; + value = sci_gpio->read_reg(addr) & GPIO_GROUP_MASK; + + while (value) { + n = __ffs(value); + value &= ~(1 << n); + n = chip->to_irq(chip, group * GPIO_GROUP_NR + n); + pr_debug("%s generic_handle_n %d\n", __FUNCTION__, n); + count++; + generic_handle_irq(n); + } + } + +} + +static irqreturn_t gpio_muxed_handler(int irq, void *dev_id) +{ + struct gpio_chip *chip = dev_id; + gpio_eic_handler(irq, chip); + return IRQ_HANDLED; +} + +/* gpio/eic cascaded irq handler */ +static void gpio_muxed_flow_handler(unsigned int irq, struct irq_desc *desc) +{ + struct gpio_chip *chip = irq_get_handler_data(irq); + gpio_eic_handler(irq, chip); +} + +static struct irqaction __d_gpio_irq = { + .name = "gpio", + .flags = IRQF_DISABLED | IRQF_NO_SUSPEND, + .handler = gpio_muxed_handler, + .dev_id = &d_sci_gpio.chip, +}; + +static struct irqaction __d_eic_irq = { + .name = "eic", + .flags = IRQF_DISABLED | IRQF_NO_SUSPEND, + .handler = gpio_muxed_handler, + .dev_id = &d_sci_eic.chip, +}; + +#if (NR_GPIO_IRQS < ARCH_NR_GPIOS) +#error "NR_GPIO_IRQS is not match with the sum of builtin/SoC GPIOs and EICs" +#endif + +#ifdef CONFIG_OF +static void gpio_irq_init(int irq, struct gpio_chip *gpiochip, + struct irq_chip *irqchip) +{ + int n, i; + struct sci_gpio_chip *sci_gpio = to_sci_gpio(gpiochip); + struct irq_domain *irq_domain = sci_gpio->irq_domain; + + /* setup the cascade irq handlers */ + if (sci_gpio->is_adi_gpio) { + /*TODO*/ irq_set_chained_handler(irq, gpio_muxed_flow_handler); + irq_set_handler_data(irq, gpiochip); + } + + for (i = 0; i < gpiochip->ngpio; i++) { + n = irq_create_mapping(irq_domain, i); + irq_set_chip_and_handler(n, irqchip, handle_level_irq); + irq_set_chip_data(n, gpiochip); + set_irq_flags(n, IRQF_VALID); + } +} +#else +static void gpio_irq_init(int irq, struct gpio_chip *gpiochip, + struct irq_chip *irqchip) +{ + int n = gpiochip->to_irq(gpiochip, 0); + int irqend = n + gpiochip->ngpio; + + /* setup the cascade irq handlers */ + if (irq >= NR_SCI_PHY_IRQS) { + irq_set_chained_handler(irq, gpio_muxed_flow_handler); + irq_set_handler_data(irq, gpiochip); + } + + for (; n < irqend; n++) { + irq_set_chip_and_handler(n, irqchip, handle_level_irq); + irq_set_chip_data(n, gpiochip); + set_irq_flags(n, IRQF_VALID); + } +} + +#endif + +#ifdef CONFIG_OF +struct sprd_gpio_match_data { + struct sci_gpio_chip *sci_gpio_chip; + struct irq_chip *irq_chip; + struct irqaction *irqaction; +}; +static struct sprd_gpio_match_data d_eic_match = { + .sci_gpio_chip = &d_sci_eic, + .irq_chip = &d_eic_irq_chip, + .irqaction = &__d_eic_irq, +}; + +static struct sprd_gpio_match_data d_gpio_match = { + .sci_gpio_chip = &d_sci_gpio, + .irq_chip = &d_gpio_irq_chip, + .irqaction = &__d_gpio_irq, +}; + +static struct sprd_gpio_match_data a_eic_match = { + .sci_gpio_chip = &a_sci_eic, + .irq_chip = &a_eic_irq_chip, + .irqaction = NULL, +}; + +static struct sprd_gpio_match_data a_gpio_match = { + .sci_gpio_chip = &a_sci_gpio, + .irq_chip = &a_gpio_irq_chip, + .irqaction = NULL, +}; + +static struct of_device_id eic_gpio_match_table[] = { + {.compatible = "sprd,d-eic-gpio",.data = &d_eic_match}, + {.compatible = "sprd,d-gpio-gpio",.data = &d_gpio_match}, + {.compatible = "sprd,a-eic-gpio",.data = &a_eic_match}, + {.compatible = "sprd,a-gpio-gpio",.data = &a_gpio_match}, + {}, +}; + +static int eic_gpio_probe(struct platform_device *pdev) +{ + struct eic_gpio_resource *r = pdev->dev.platform_data; + const struct of_device_id *match; + struct sprd_gpio_match_data *match_data; + struct device_node *np = pdev->dev.of_node; + struct sci_gpio_chip *sgc; + struct irq_chip *ic; + struct irqaction *ia; + static bool init_done = false; + struct resource *res; + struct irq_domain *irq_domain; + int irq; + + if (!np && !r) + BUG(); + + if (!init_done) { + /* enable EIC */ +#if defined(CONFIG_ARCH_SC8825) + sci_glb_set(REG_GLB_GEN0, BIT_EIC_EB); + sci_glb_set(REG_GLB_GEN0, BIT_GPIO_EB); + sci_glb_set(REG_GLB_GEN0, BIT_RTC_EIC_EB); + sci_adi_set(ANA_REG_GLB_ANA_APB_CLK_EN, + BIT_ANA_EIC_EB | BIT_ANA_GPIO_EB | BIT_ANA_RTC_EIC_EB); +#elif (defined(CONFIG_ARCH_SCX15)||defined(CONFIG_ADIE_SC2723S)||defined(CONFIG_ADIE_SC2723)) + sci_glb_set(REG_AON_APB_APB_EB0, BIT_GPIO_EB | BIT_EIC_EB); + sci_glb_set(REG_AON_APB_APB_RTC_EB, BIT_EIC_RTC_EB); + sci_adi_set(ANA_REG_GLB_ARM_MODULE_EN, BIT_ANA_EIC_EN); + sci_adi_set(ANA_REG_GLB_RTC_CLK_EN, BIT_RTC_EIC_EN); +#elif defined(CONFIG_ARCH_SCX35) + sci_glb_set(REG_AON_APB_APB_EB0, BIT_GPIO_EB | BIT_EIC_EB); + sci_glb_set(REG_AON_APB_APB_RTC_EB, BIT_EIC_RTC_EB); + sci_adi_set(ANA_REG_GLB_ARM_MODULE_EN, + BIT_ANA_EIC_EN | BIT_ANA_GPIO_EN); + sci_adi_set(ANA_REG_GLB_RTC_CLK_EN, BIT_RTC_EIC_EN); +#endif + init_done = true; + } + + match = of_match_device(eic_gpio_match_table, &pdev->dev); + match_data = (struct sprd_gpio_match_data *)(match->data); + sgc = match_data->sci_gpio_chip; + ic = match_data->irq_chip; + ia = match_data->irqaction; + + if(match_data==&a_eic_match) + sgc->base_addr = ANA_EIC_BASE; + else + { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "No reg of property specified\n"); + return -ENODEV; + } + + sgc->base_addr = (unsigned long)ioremap_nocache(res->start, + res->end - res->start); + if (!sgc->base_addr) + panic("ioremap failed!\n"); + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) + irq = -1; + else + irq = res->start; + + if (of_property_read_u32(np, "ngpios", (u32 *)(&sgc->chip.ngpio))) { + dev_err(&pdev->dev, "No ngpios of property specified\n"); + release_mem_region(res->start, resource_size(res)); + return -ENODEV; + } + if (of_property_read_u32(np, "gpiobase", (u32 *)(&sgc->chip.base))) { + dev_err(&pdev->dev, "No gpiobase of property specified\n"); + sgc->chip.base = -1; + } + + dev_info(&pdev->dev, "base_addr %lx, gpio base %d, ngpio %d, irq %d", + sgc->base_addr, sgc->chip.base, sgc->chip.ngpio, irq); + +#ifdef CONFIG_OF + sgc->chip.of_node = np; +#endif + gpiochip_add(&sgc->chip); + + irq_domain = irq_domain_add_linear(np, sgc->chip.ngpio, + &irq_domain_simple_ops, NULL); + if (!irq_domain) { + dev_err(&pdev->dev, "failed to add irq domain\n"); + gpiochip_remove(&sgc->chip); + release_mem_region(res->start, resource_size(res)); + return -ENODEV; + } + sgc->irq_domain = irq_domain; + + if (-1 != irq) { + gpio_irq_init(irq, &sgc->chip, ic); + if (ia != NULL) + setup_irq(irq, ia); + } + + return 0; +} + +static int eic_gpio_remove(struct platform_device *pdev) +{ + int ret = 0; + const struct of_device_id *match; + struct sprd_gpio_match_data *match_data; + struct sci_gpio_chip *sgc; + + match = of_match_device(eic_gpio_match_table, &pdev->dev); + match_data = (struct sprd_gpio_match_data *)(match->data); + sgc = match_data->sci_gpio_chip; + ret = gpiochip_remove(&sgc->chip); + + return ret; +} +#else +static int eic_gpio_probe(struct platform_device *pdev) +{ + struct eic_gpio_resource *r = pdev->dev.platform_data; + struct irq_domain *irq_domain; + if (!r) + BUG(); + + /* enable EIC */ +#if defined(CONFIG_ARCH_SC8825) + sci_glb_set(REG_GLB_GEN0, BIT_EIC_EB); + sci_glb_set(REG_GLB_GEN0, BIT_GPIO_EB); + sci_glb_set(REG_GLB_GEN0, BIT_RTC_EIC_EB); + sci_adi_set(ANA_REG_GLB_ANA_APB_CLK_EN, + BIT_ANA_EIC_EB | BIT_ANA_GPIO_EB | BIT_ANA_RTC_EIC_EB); +#elif (defined(CONFIG_ARCH_SCX15)||defined(CONFIG_ADIE_SC2723S)||defined(CONFIG_ADIE_SC2723)) + sci_glb_set(REG_AON_APB_APB_EB0, BIT_GPIO_EB | BIT_EIC_EB); + sci_glb_set(REG_AON_APB_APB_RTC_EB, BIT_EIC_RTC_EB); + sci_adi_set(ANA_REG_GLB_ARM_MODULE_EN, BIT_ANA_EIC_EN); + sci_adi_set(ANA_REG_GLB_RTC_CLK_EN, BIT_RTC_EIC_EN); +#elif defined(CONFIG_ARCH_SCX35) + sci_glb_set(REG_AON_APB_APB_EB0, BIT_GPIO_EB | BIT_EIC_EB); + sci_glb_set(REG_AON_APB_APB_RTC_EB, BIT_EIC_RTC_EB); + sci_adi_set(ANA_REG_GLB_ARM_MODULE_EN, + BIT_ANA_EIC_EN | BIT_ANA_GPIO_EN); + sci_adi_set(ANA_REG_GLB_RTC_CLK_EN, BIT_RTC_EIC_EN); +#endif + + d_sci_gpio.base_addr = r[ENUM_ID_D_GPIO].base_addr; + d_sci_gpio.chip.base = r[ENUM_ID_D_GPIO].chip_base; + d_sci_gpio.chip.ngpio = r[ENUM_ID_D_GPIO].chip_ngpio; + + d_sci_eic.base_addr = r[ENUM_ID_D_EIC].base_addr; + d_sci_eic.chip.base = r[ENUM_ID_D_EIC].chip_base; + d_sci_eic.chip.ngpio = r[ENUM_ID_D_EIC].chip_ngpio; + + a_sci_gpio.base_addr = r[ENUM_ID_A_GPIO].base_addr; + a_sci_gpio.chip.base = r[ENUM_ID_A_GPIO].chip_base; + a_sci_gpio.chip.ngpio = r[ENUM_ID_A_GPIO].chip_ngpio; + + a_sci_eic.base_addr = r[ENUM_ID_A_EIC].base_addr; + a_sci_eic.chip.base = r[ENUM_ID_A_EIC].chip_base; + a_sci_eic.chip.ngpio = r[ENUM_ID_A_EIC].chip_ngpio; + + if (d_sci_eic.chip.ngpio > 0) + gpiochip_add(&d_sci_eic.chip); + + if (d_sci_gpio.chip.ngpio > 0) + gpiochip_add(&d_sci_gpio.chip); + + if (a_sci_eic.chip.ngpio > 0) + gpiochip_add(&a_sci_eic.chip); + + if (a_sci_gpio.chip.ngpio > 0) + gpiochip_add(&a_sci_gpio.chip); + + if (-1 != r[ENUM_ID_D_GPIO].irq) { + gpio_irq_init(r[ENUM_ID_D_GPIO].irq, &d_sci_gpio.chip, + &d_gpio_irq_chip); + setup_irq(r[ENUM_ID_D_GPIO].irq, &__d_gpio_irq); + } + + if (-1 != r[ENUM_ID_D_EIC].irq) { + gpio_irq_init(r[ENUM_ID_D_EIC].irq, &d_sci_eic.chip, + &d_eic_irq_chip); + setup_irq(r[ENUM_ID_D_EIC].irq, &__d_eic_irq); + } + + if (-1 != r[ENUM_ID_A_GPIO].irq) + gpio_irq_init(r[ENUM_ID_A_GPIO].irq, &a_sci_gpio.chip, + &a_gpio_irq_chip); + + if (-1 != r[ENUM_ID_A_EIC].irq) + gpio_irq_init(r[ENUM_ID_A_EIC].irq, &a_sci_eic.chip, + &a_eic_irq_chip); + + return 0; +} + +static int eic_gpio_remove(struct platform_device *pdev) +{ + int ret = 0; + + if (d_sci_eic.chip.ngpio > 0) + ret += gpiochip_remove(&d_sci_eic.chip); + if (d_sci_gpio.chip.ngpio > 0) + ret += gpiochip_remove(&d_sci_gpio.chip); + if (a_sci_eic.chip.ngpio > 0) + ret += gpiochip_remove(&a_sci_eic.chip); + if (a_sci_gpio.chip.ngpio > 0) + ret += gpiochip_remove(&a_sci_gpio.chip); + + return ret; +} +#endif + +static struct platform_driver eic_gpio_driver = { + .driver.name = "eic-gpio", + .driver.owner = THIS_MODULE, + .driver.of_match_table = of_match_ptr(eic_gpio_match_table), + .probe = eic_gpio_probe, + .remove = eic_gpio_remove, +}; + +static int __init eic_gpio_init(void) +{ + return platform_driver_register(&eic_gpio_driver); +} + +postcore_initcall(eic_gpio_init); + +static void __exit eic_gpio_exit(void) +{ + return platform_driver_unregister(&eic_gpio_driver); +} + +module_exit(eic_gpio_exit); |
