244 lines
		
	
	
	
		
			6.1 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			244 lines
		
	
	
	
		
			6.1 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * 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); |