|  | /* | 
|  | * Toumaz Xenif TZ1090 PDC GPIO handling. | 
|  | * | 
|  | * Copyright (C) 2012-2013 Imagination Technologies Ltd. | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or modify | 
|  | * it under the terms of the GNU General Public License version 2 as | 
|  | * published by the Free Software Foundation. | 
|  | */ | 
|  |  | 
|  | #include <linux/bitops.h> | 
|  | #include <linux/gpio.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/of_irq.h> | 
|  | #include <linux/pinctrl/consumer.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/slab.h> | 
|  | #include <linux/syscore_ops.h> | 
|  | #include <asm/global_lock.h> | 
|  |  | 
|  | /* Register offsets from SOC_GPIO_CONTROL0 */ | 
|  | #define REG_SOC_GPIO_CONTROL0	0x00 | 
|  | #define REG_SOC_GPIO_CONTROL1	0x04 | 
|  | #define REG_SOC_GPIO_CONTROL2	0x08 | 
|  | #define REG_SOC_GPIO_CONTROL3	0x0c | 
|  | #define REG_SOC_GPIO_STATUS	0x80 | 
|  |  | 
|  | /* PDC GPIOs go after normal GPIOs */ | 
|  | #define GPIO_PDC_BASE		90 | 
|  | #define GPIO_PDC_NGPIO		7 | 
|  |  | 
|  | /* Out of PDC gpios, only syswakes have irqs */ | 
|  | #define GPIO_PDC_IRQ_FIRST	2 | 
|  | #define GPIO_PDC_NIRQ		3 | 
|  |  | 
|  | /** | 
|  | * struct tz1090_pdc_gpio - GPIO bank private data | 
|  | * @chip:	Generic GPIO chip for GPIO bank | 
|  | * @reg:	Base of registers, offset for this GPIO bank | 
|  | * @irq:	IRQ numbers for Syswake GPIOs | 
|  | * | 
|  | * This is the main private data for the PDC GPIO driver. It encapsulates a | 
|  | * gpio_chip, and the callbacks for the gpio_chip can access the private data | 
|  | * with the to_pdc() macro below. | 
|  | */ | 
|  | struct tz1090_pdc_gpio { | 
|  | struct gpio_chip chip; | 
|  | void __iomem *reg; | 
|  | int irq[GPIO_PDC_NIRQ]; | 
|  | }; | 
|  | #define to_pdc(c)	container_of(c, struct tz1090_pdc_gpio, chip) | 
|  |  | 
|  | /* Register accesses into the PDC MMIO area */ | 
|  |  | 
|  | static inline void pdc_write(struct tz1090_pdc_gpio *priv, unsigned int reg_offs, | 
|  | unsigned int data) | 
|  | { | 
|  | writel(data, priv->reg + reg_offs); | 
|  | } | 
|  |  | 
|  | static inline unsigned int pdc_read(struct tz1090_pdc_gpio *priv, | 
|  | unsigned int reg_offs) | 
|  | { | 
|  | return readl(priv->reg + reg_offs); | 
|  | } | 
|  |  | 
|  | /* Generic GPIO interface */ | 
|  |  | 
|  | static int tz1090_pdc_gpio_direction_input(struct gpio_chip *chip, | 
|  | unsigned int offset) | 
|  | { | 
|  | struct tz1090_pdc_gpio *priv = to_pdc(chip); | 
|  | u32 value; | 
|  | int lstat; | 
|  |  | 
|  | __global_lock2(lstat); | 
|  | value = pdc_read(priv, REG_SOC_GPIO_CONTROL1); | 
|  | value |= BIT(offset); | 
|  | pdc_write(priv, REG_SOC_GPIO_CONTROL1, value); | 
|  | __global_unlock2(lstat); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tz1090_pdc_gpio_direction_output(struct gpio_chip *chip, | 
|  | unsigned int offset, | 
|  | int output_value) | 
|  | { | 
|  | struct tz1090_pdc_gpio *priv = to_pdc(chip); | 
|  | u32 value; | 
|  | int lstat; | 
|  |  | 
|  | __global_lock2(lstat); | 
|  | /* EXT_POWER doesn't seem to have an output value bit */ | 
|  | if (offset < 6) { | 
|  | value = pdc_read(priv, REG_SOC_GPIO_CONTROL0); | 
|  | if (output_value) | 
|  | value |= BIT(offset); | 
|  | else | 
|  | value &= ~BIT(offset); | 
|  | pdc_write(priv, REG_SOC_GPIO_CONTROL0, value); | 
|  | } | 
|  |  | 
|  | value = pdc_read(priv, REG_SOC_GPIO_CONTROL1); | 
|  | value &= ~BIT(offset); | 
|  | pdc_write(priv, REG_SOC_GPIO_CONTROL1, value); | 
|  | __global_unlock2(lstat); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int tz1090_pdc_gpio_get(struct gpio_chip *chip, unsigned int offset) | 
|  | { | 
|  | struct tz1090_pdc_gpio *priv = to_pdc(chip); | 
|  | return pdc_read(priv, REG_SOC_GPIO_STATUS) & BIT(offset); | 
|  | } | 
|  |  | 
|  | static void tz1090_pdc_gpio_set(struct gpio_chip *chip, unsigned int offset, | 
|  | int output_value) | 
|  | { | 
|  | struct tz1090_pdc_gpio *priv = to_pdc(chip); | 
|  | u32 value; | 
|  | int lstat; | 
|  |  | 
|  | /* EXT_POWER doesn't seem to have an output value bit */ | 
|  | if (offset >= 6) | 
|  | return; | 
|  |  | 
|  | __global_lock2(lstat); | 
|  | value = pdc_read(priv, REG_SOC_GPIO_CONTROL0); | 
|  | if (output_value) | 
|  | value |= BIT(offset); | 
|  | else | 
|  | value &= ~BIT(offset); | 
|  | pdc_write(priv, REG_SOC_GPIO_CONTROL0, value); | 
|  | __global_unlock2(lstat); | 
|  | } | 
|  |  | 
|  | static int tz1090_pdc_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) | 
|  | { | 
|  | struct tz1090_pdc_gpio *priv = to_pdc(chip); | 
|  | unsigned int syswake = offset - GPIO_PDC_IRQ_FIRST; | 
|  | int irq; | 
|  |  | 
|  | /* only syswakes have irqs */ | 
|  | if (syswake >= GPIO_PDC_NIRQ) | 
|  | return -EINVAL; | 
|  |  | 
|  | irq = priv->irq[syswake]; | 
|  | if (!irq) | 
|  | return -EINVAL; | 
|  |  | 
|  | return irq; | 
|  | } | 
|  |  | 
|  | static int tz1090_pdc_gpio_probe(struct platform_device *pdev) | 
|  | { | 
|  | struct device_node *np = pdev->dev.of_node; | 
|  | struct resource *res_regs; | 
|  | struct tz1090_pdc_gpio *priv; | 
|  | unsigned int i; | 
|  |  | 
|  | if (!np) { | 
|  | dev_err(&pdev->dev, "must be instantiated via devicetree\n"); | 
|  | return -ENOENT; | 
|  | } | 
|  |  | 
|  | res_regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
|  | if (!res_regs) { | 
|  | dev_err(&pdev->dev, "cannot find registers resource\n"); | 
|  | return -ENOENT; | 
|  | } | 
|  |  | 
|  | priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); | 
|  | if (!priv) { | 
|  | dev_err(&pdev->dev, "unable to allocate driver data\n"); | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | /* Ioremap the registers */ | 
|  | priv->reg = devm_ioremap(&pdev->dev, res_regs->start, | 
|  | resource_size(res_regs)); | 
|  | if (!priv->reg) { | 
|  | dev_err(&pdev->dev, "unable to ioremap registers\n"); | 
|  | return -ENOMEM; | 
|  | } | 
|  |  | 
|  | /* Set up GPIO chip */ | 
|  | priv->chip.label		= "tz1090-pdc-gpio"; | 
|  | priv->chip.dev			= &pdev->dev; | 
|  | priv->chip.direction_input	= tz1090_pdc_gpio_direction_input; | 
|  | priv->chip.direction_output	= tz1090_pdc_gpio_direction_output; | 
|  | priv->chip.get			= tz1090_pdc_gpio_get; | 
|  | priv->chip.set			= tz1090_pdc_gpio_set; | 
|  | priv->chip.free			= gpiochip_generic_free; | 
|  | priv->chip.request		= gpiochip_generic_request; | 
|  | priv->chip.to_irq		= tz1090_pdc_gpio_to_irq; | 
|  | priv->chip.of_node		= np; | 
|  |  | 
|  | /* GPIO numbering */ | 
|  | priv->chip.base			= GPIO_PDC_BASE; | 
|  | priv->chip.ngpio		= GPIO_PDC_NGPIO; | 
|  |  | 
|  | /* Map the syswake irqs */ | 
|  | for (i = 0; i < GPIO_PDC_NIRQ; ++i) | 
|  | priv->irq[i] = irq_of_parse_and_map(np, i); | 
|  |  | 
|  | /* Add the GPIO bank */ | 
|  | gpiochip_add(&priv->chip); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct of_device_id tz1090_pdc_gpio_of_match[] = { | 
|  | { .compatible = "img,tz1090-pdc-gpio" }, | 
|  | { }, | 
|  | }; | 
|  |  | 
|  | static struct platform_driver tz1090_pdc_gpio_driver = { | 
|  | .driver = { | 
|  | .name		= "tz1090-pdc-gpio", | 
|  | .of_match_table	= tz1090_pdc_gpio_of_match, | 
|  | }, | 
|  | .probe		= tz1090_pdc_gpio_probe, | 
|  | }; | 
|  |  | 
|  | static int __init tz1090_pdc_gpio_init(void) | 
|  | { | 
|  | return platform_driver_register(&tz1090_pdc_gpio_driver); | 
|  | } | 
|  | subsys_initcall(tz1090_pdc_gpio_init); |