| /* | 
 |  * 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_request(struct gpio_chip *chip, unsigned int offset) | 
 | { | 
 | 	return pinctrl_request_gpio(chip->base + offset); | 
 | } | 
 |  | 
 | static void tz1090_pdc_gpio_free(struct gpio_chip *chip, unsigned int offset) | 
 | { | 
 | 	pinctrl_free_gpio(chip->base + offset); | 
 | } | 
 |  | 
 | 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, | 
 | 				 res_regs->end - res_regs->start); | 
 | 	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			= tz1090_pdc_gpio_free; | 
 | 	priv->chip.request		= tz1090_pdc_gpio_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", | 
 | 		.owner		= THIS_MODULE, | 
 | 		.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); |