| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Pinctrl GPIO driver for Intel Baytrail | 
					
						
							|  |  |  |  * Copyright (c) 2012-2013, Intel Corporation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Author: Mathias Nyman <mathias.nyman@linux.intel.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is free software; you can redistribute it and/or modify it | 
					
						
							|  |  |  |  * under the terms and conditions of the GNU General Public License, | 
					
						
							|  |  |  |  * version 2, as published by the Free Software Foundation. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * This program is distributed in the hope 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. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * You should have received a copy of the GNU General Public License along with | 
					
						
							|  |  |  |  * this program; if not, write to the Free Software Foundation, Inc., | 
					
						
							|  |  |  |  * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/module.h>
 | 
					
						
							|  |  |  | #include <linux/init.h>
 | 
					
						
							|  |  |  | #include <linux/types.h>
 | 
					
						
							|  |  |  | #include <linux/bitops.h>
 | 
					
						
							|  |  |  | #include <linux/interrupt.h>
 | 
					
						
							|  |  |  | #include <linux/irq.h>
 | 
					
						
							|  |  |  | #include <linux/gpio.h>
 | 
					
						
							|  |  |  | #include <linux/irqdomain.h>
 | 
					
						
							|  |  |  | #include <linux/acpi.h>
 | 
					
						
							|  |  |  | #include <linux/acpi_gpio.h>
 | 
					
						
							|  |  |  | #include <linux/platform_device.h>
 | 
					
						
							|  |  |  | #include <linux/seq_file.h>
 | 
					
						
							|  |  |  | #include <linux/io.h>
 | 
					
						
							|  |  |  | #include <linux/pm_runtime.h>
 | 
					
						
							|  |  |  | #include <linux/pinctrl/pinctrl.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* memory mapped register offsets */ | 
					
						
							|  |  |  | #define BYT_CONF0_REG		0x000
 | 
					
						
							|  |  |  | #define BYT_CONF1_REG		0x004
 | 
					
						
							|  |  |  | #define BYT_VAL_REG		0x008
 | 
					
						
							|  |  |  | #define BYT_DFT_REG		0x00c
 | 
					
						
							|  |  |  | #define BYT_INT_STAT_REG	0x800
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* BYT_CONF0_REG register bits */ | 
					
						
							|  |  |  | #define BYT_TRIG_NEG		BIT(26)
 | 
					
						
							|  |  |  | #define BYT_TRIG_POS		BIT(25)
 | 
					
						
							|  |  |  | #define BYT_TRIG_LVL		BIT(24)
 | 
					
						
							|  |  |  | #define BYT_PIN_MUX		0x07
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* BYT_VAL_REG register bits */ | 
					
						
							|  |  |  | #define BYT_INPUT_EN		BIT(2)  /* 0: input enabled (active low)*/
 | 
					
						
							|  |  |  | #define BYT_OUTPUT_EN		BIT(1)  /* 0: output enabled (active low)*/
 | 
					
						
							|  |  |  | #define BYT_LEVEL		BIT(0)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define BYT_DIR_MASK		(BIT(1) | BIT(2))
 | 
					
						
							|  |  |  | #define BYT_TRIG_MASK		(BIT(26) | BIT(25) | BIT(24))
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define BYT_NGPIO_SCORE		102
 | 
					
						
							|  |  |  | #define BYT_NGPIO_NCORE		28
 | 
					
						
							|  |  |  | #define BYT_NGPIO_SUS		44
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Baytrail gpio controller consist of three separate sub-controllers called | 
					
						
							|  |  |  |  * SCORE, NCORE and SUS. The sub-controllers are identified by their acpi UID. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * GPIO numbering is _not_ ordered meaning that gpio # 0 in ACPI namespace does | 
					
						
							|  |  |  |  * _not_ correspond to the first gpio register at controller's gpio base. | 
					
						
							|  |  |  |  * There is no logic or pattern in mapping gpio numbers to registers (pads) so | 
					
						
							|  |  |  |  * each sub-controller needs to have its own mapping table | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* score_pins[gpio_nr] = pad_nr */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static unsigned const score_pins[BYT_NGPIO_SCORE] = { | 
					
						
							|  |  |  | 	85, 89, 93, 96, 99, 102, 98, 101, 34, 37, | 
					
						
							|  |  |  | 	36, 38, 39, 35, 40, 84, 62, 61, 64, 59, | 
					
						
							|  |  |  | 	54, 56, 60, 55, 63, 57, 51, 50, 53, 47, | 
					
						
							|  |  |  | 	52, 49, 48, 43, 46, 41, 45, 42, 58, 44, | 
					
						
							|  |  |  | 	95, 105, 70, 68, 67, 66, 69, 71, 65, 72, | 
					
						
							|  |  |  | 	86, 90, 88, 92, 103, 77, 79, 83, 78, 81, | 
					
						
							|  |  |  | 	80, 82, 13, 12, 15, 14, 17, 18, 19, 16, | 
					
						
							|  |  |  | 	2, 1, 0, 4, 6, 7, 9, 8, 33, 32, | 
					
						
							|  |  |  | 	31, 30, 29, 27, 25, 28, 26, 23, 21, 20, | 
					
						
							|  |  |  | 	24, 22, 5, 3, 10, 11, 106, 87, 91, 104, | 
					
						
							|  |  |  | 	97, 100, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static unsigned const ncore_pins[BYT_NGPIO_NCORE] = { | 
					
						
							|  |  |  | 	19, 18, 17, 20, 21, 22, 24, 25, 23, 16, | 
					
						
							|  |  |  | 	14, 15, 12, 26, 27, 1, 4, 8, 11, 0, | 
					
						
							|  |  |  | 	3, 6, 10, 13, 2, 5, 9, 7, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static unsigned const sus_pins[BYT_NGPIO_SUS] = { | 
					
						
							|  |  |  | 	29, 33, 30, 31, 32, 34, 36, 35, 38, 37, | 
					
						
							|  |  |  | 	18, 7, 11, 20, 17, 1, 8, 10, 19, 12, | 
					
						
							|  |  |  | 	0, 2, 23, 39, 28, 27, 22, 21, 24, 25, | 
					
						
							|  |  |  | 	26, 51, 56, 54, 49, 55, 48, 57, 50, 58, | 
					
						
							|  |  |  | 	52, 53, 59, 40, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct pinctrl_gpio_range byt_ranges[] = { | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		.name = "1", /* match with acpi _UID in probe */ | 
					
						
							|  |  |  | 		.npins = BYT_NGPIO_SCORE, | 
					
						
							|  |  |  | 		.pins = score_pins, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		.name = "2", | 
					
						
							|  |  |  | 		.npins = BYT_NGPIO_NCORE, | 
					
						
							|  |  |  | 		.pins = ncore_pins, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		.name = "3", | 
					
						
							|  |  |  | 		.npins = BYT_NGPIO_SUS, | 
					
						
							|  |  |  | 		.pins = sus_pins, | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct byt_gpio { | 
					
						
							|  |  |  | 	struct gpio_chip		chip; | 
					
						
							|  |  |  | 	struct irq_domain		*domain; | 
					
						
							|  |  |  | 	struct platform_device		*pdev; | 
					
						
							|  |  |  | 	spinlock_t			lock; | 
					
						
							|  |  |  | 	void __iomem			*reg_base; | 
					
						
							|  |  |  | 	struct pinctrl_gpio_range	*range; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-10 14:55:39 +03:00
										 |  |  | #define to_byt_gpio(c)	container_of(c, struct byt_gpio, chip)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | static void __iomem *byt_gpio_reg(struct gpio_chip *chip, unsigned offset, | 
					
						
							|  |  |  | 				 int reg) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-10 14:55:39 +03:00
										 |  |  | 	struct byt_gpio *vg = to_byt_gpio(chip); | 
					
						
							| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | 	u32 reg_offset; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (reg == BYT_INT_STAT_REG) | 
					
						
							|  |  |  | 		reg_offset = (offset / 32) * 4; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		reg_offset = vg->range->pins[offset] * 16; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-10 14:55:38 +03:00
										 |  |  | 	return vg->reg_base + reg_offset + reg; | 
					
						
							| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int byt_gpio_request(struct gpio_chip *chip, unsigned offset) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-10 14:55:39 +03:00
										 |  |  | 	struct byt_gpio *vg = to_byt_gpio(chip); | 
					
						
							| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	pm_runtime_get(&vg->pdev->dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void byt_gpio_free(struct gpio_chip *chip, unsigned offset) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-10 14:55:39 +03:00
										 |  |  | 	struct byt_gpio *vg = to_byt_gpio(chip); | 
					
						
							| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | 	void __iomem *reg = byt_gpio_reg(&vg->chip, offset, BYT_CONF0_REG); | 
					
						
							|  |  |  | 	u32 value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* clear interrupt triggering */ | 
					
						
							|  |  |  | 	value = readl(reg); | 
					
						
							|  |  |  | 	value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); | 
					
						
							|  |  |  | 	writel(value, reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pm_runtime_put(&vg->pdev->dev); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int byt_irq_type(struct irq_data *d, unsigned type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct byt_gpio *vg = irq_data_get_irq_chip_data(d); | 
					
						
							|  |  |  | 	u32 offset = irqd_to_hwirq(d); | 
					
						
							|  |  |  | 	u32 value; | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	void __iomem *reg = byt_gpio_reg(&vg->chip, offset, BYT_CONF0_REG); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (offset >= vg->chip.ngpio) | 
					
						
							|  |  |  | 		return -EINVAL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&vg->lock, flags); | 
					
						
							|  |  |  | 	value = readl(reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* For level trigges the BYT_TRIG_POS and BYT_TRIG_NEG bits
 | 
					
						
							|  |  |  | 	 * are used to indicate high and low level triggering | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	value &= ~(BYT_TRIG_POS | BYT_TRIG_NEG | BYT_TRIG_LVL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (type) { | 
					
						
							|  |  |  | 	case IRQ_TYPE_LEVEL_HIGH: | 
					
						
							|  |  |  | 		value |= BYT_TRIG_LVL; | 
					
						
							|  |  |  | 	case IRQ_TYPE_EDGE_RISING: | 
					
						
							|  |  |  | 		value |= BYT_TRIG_POS; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case IRQ_TYPE_LEVEL_LOW: | 
					
						
							|  |  |  | 		value |= BYT_TRIG_LVL; | 
					
						
							|  |  |  | 	case IRQ_TYPE_EDGE_FALLING: | 
					
						
							|  |  |  | 		value |= BYT_TRIG_NEG; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case IRQ_TYPE_EDGE_BOTH: | 
					
						
							|  |  |  | 		value |= (BYT_TRIG_NEG | BYT_TRIG_POS); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	writel(value, reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&vg->lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int byt_gpio_get(struct gpio_chip *chip, unsigned offset) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	void __iomem *reg = byt_gpio_reg(chip, offset, BYT_VAL_REG); | 
					
						
							|  |  |  | 	return readl(reg) & BYT_LEVEL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void byt_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-10 14:55:39 +03:00
										 |  |  | 	struct byt_gpio *vg = to_byt_gpio(chip); | 
					
						
							| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | 	void __iomem *reg = byt_gpio_reg(chip, offset, BYT_VAL_REG); | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	u32 old_val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&vg->lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	old_val = readl(reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (value) | 
					
						
							|  |  |  | 		writel(old_val | BYT_LEVEL, reg); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		writel(old_val & ~BYT_LEVEL, reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&vg->lock, flags); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int byt_gpio_direction_input(struct gpio_chip *chip, unsigned offset) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-10 14:55:39 +03:00
										 |  |  | 	struct byt_gpio *vg = to_byt_gpio(chip); | 
					
						
							| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | 	void __iomem *reg = byt_gpio_reg(chip, offset, BYT_VAL_REG); | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	u32 value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&vg->lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	value = readl(reg) | BYT_DIR_MASK; | 
					
						
							| 
									
										
										
										
											2013-07-10 14:55:40 +03:00
										 |  |  | 	value &= ~BYT_INPUT_EN;		/* active low */ | 
					
						
							| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | 	writel(value, reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&vg->lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int byt_gpio_direction_output(struct gpio_chip *chip, | 
					
						
							|  |  |  | 				     unsigned gpio, int value) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-10 14:55:39 +03:00
										 |  |  | 	struct byt_gpio *vg = to_byt_gpio(chip); | 
					
						
							| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | 	void __iomem *reg = byt_gpio_reg(chip, gpio, BYT_VAL_REG); | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	u32 reg_val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&vg->lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-10 14:55:40 +03:00
										 |  |  | 	reg_val = readl(reg) | BYT_DIR_MASK; | 
					
						
							|  |  |  | 	reg_val &= ~BYT_OUTPUT_EN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (value) | 
					
						
							|  |  |  | 		writel(reg_val | BYT_LEVEL, reg); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		writel(reg_val & ~BYT_LEVEL, reg); | 
					
						
							| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&vg->lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void byt_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-10 14:55:39 +03:00
										 |  |  | 	struct byt_gpio *vg = to_byt_gpio(chip); | 
					
						
							| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | 	int i; | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 	u32 conf0, val, offs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_irqsave(&vg->lock, flags); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < vg->chip.ngpio; i++) { | 
					
						
							|  |  |  | 		offs = vg->range->pins[i] * 16; | 
					
						
							|  |  |  | 		conf0 = readl(vg->reg_base + offs + BYT_CONF0_REG); | 
					
						
							|  |  |  | 		val = readl(vg->reg_base + offs + BYT_VAL_REG); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		seq_printf(s, | 
					
						
							|  |  |  | 			   " gpio-%-3d %s %s %s pad-%-3d offset:0x%03x mux:%d %s%s%s\n", | 
					
						
							|  |  |  | 			   i, | 
					
						
							|  |  |  | 			   val & BYT_INPUT_EN ? "  " : "in", | 
					
						
							|  |  |  | 			   val & BYT_OUTPUT_EN ? "   " : "out", | 
					
						
							|  |  |  | 			   val & BYT_LEVEL ? "hi" : "lo", | 
					
						
							|  |  |  | 			   vg->range->pins[i], offs, | 
					
						
							|  |  |  | 			   conf0 & 0x7, | 
					
						
							| 
									
										
										
										
											2013-07-10 16:42:14 +03:00
										 |  |  | 			   conf0 & BYT_TRIG_NEG ? " fall" : "", | 
					
						
							|  |  |  | 			   conf0 & BYT_TRIG_POS ? " rise" : "", | 
					
						
							|  |  |  | 			   conf0 & BYT_TRIG_LVL ? " level" : ""); | 
					
						
							| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	spin_unlock_irqrestore(&vg->lock, flags); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int byt_gpio_to_irq(struct gpio_chip *chip, unsigned offset) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-07-10 14:55:39 +03:00
										 |  |  | 	struct byt_gpio *vg = to_byt_gpio(chip); | 
					
						
							| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | 	return irq_create_mapping(vg->domain, offset); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void byt_gpio_irq_handler(unsigned irq, struct irq_desc *desc) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct irq_data *data = irq_desc_get_irq_data(desc); | 
					
						
							|  |  |  | 	struct byt_gpio *vg = irq_data_get_irq_handler_data(data); | 
					
						
							|  |  |  | 	struct irq_chip *chip = irq_data_get_irq_chip(data); | 
					
						
							|  |  |  | 	u32 base, pin, mask; | 
					
						
							|  |  |  | 	void __iomem *reg; | 
					
						
							|  |  |  | 	u32 pending; | 
					
						
							|  |  |  | 	unsigned virq; | 
					
						
							|  |  |  | 	int looplimit = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* check from GPIO controller which pin triggered the interrupt */ | 
					
						
							|  |  |  | 	for (base = 0; base < vg->chip.ngpio; base += 32) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		reg = byt_gpio_reg(&vg->chip, base, BYT_INT_STAT_REG); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		while ((pending = readl(reg))) { | 
					
						
							|  |  |  | 			pin = __ffs(pending); | 
					
						
							|  |  |  | 			mask = BIT(pin); | 
					
						
							|  |  |  | 			/* Clear before handling so we can't lose an edge */ | 
					
						
							|  |  |  | 			writel(mask, reg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			virq = irq_find_mapping(vg->domain, base + pin); | 
					
						
							|  |  |  | 			generic_handle_irq(virq); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* In case bios or user sets triggering incorretly a pin
 | 
					
						
							|  |  |  | 			 * might remain in "interrupt triggered" state. | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			if (looplimit++ > 32) { | 
					
						
							|  |  |  | 				dev_err(&vg->pdev->dev, | 
					
						
							|  |  |  | 					"Gpio %d interrupt flood, disabling\n", | 
					
						
							|  |  |  | 					base + pin); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				reg = byt_gpio_reg(&vg->chip, base + pin, | 
					
						
							|  |  |  | 						   BYT_CONF0_REG); | 
					
						
							|  |  |  | 				mask = readl(reg); | 
					
						
							|  |  |  | 				mask &= ~(BYT_TRIG_NEG | BYT_TRIG_POS | | 
					
						
							|  |  |  | 					  BYT_TRIG_LVL); | 
					
						
							|  |  |  | 				writel(mask, reg); | 
					
						
							|  |  |  | 				mask = readl(reg); /* flush */ | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	chip->irq_eoi(data); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void byt_irq_unmask(struct irq_data *d) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void byt_irq_mask(struct irq_data *d) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct irq_chip byt_irqchip = { | 
					
						
							|  |  |  | 	.name = "BYT-GPIO", | 
					
						
							|  |  |  | 	.irq_mask = byt_irq_mask, | 
					
						
							|  |  |  | 	.irq_unmask = byt_irq_unmask, | 
					
						
							|  |  |  | 	.irq_set_type = byt_irq_type, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void byt_gpio_irq_init_hw(struct byt_gpio *vg) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	void __iomem *reg; | 
					
						
							|  |  |  | 	u32 base, value; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* clear interrupt status trigger registers */ | 
					
						
							|  |  |  | 	for (base = 0; base < vg->chip.ngpio; base += 32) { | 
					
						
							|  |  |  | 		reg = byt_gpio_reg(&vg->chip, base, BYT_INT_STAT_REG); | 
					
						
							|  |  |  | 		writel(0xffffffff, reg); | 
					
						
							|  |  |  | 		/* make sure trigger bits are cleared, if not then a pin
 | 
					
						
							|  |  |  | 		   might be misconfigured in bios */ | 
					
						
							|  |  |  | 		value = readl(reg); | 
					
						
							|  |  |  | 		if (value) | 
					
						
							|  |  |  | 			dev_err(&vg->pdev->dev, | 
					
						
							|  |  |  | 				"GPIO interrupt error, pins misconfigured\n"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int byt_gpio_irq_map(struct irq_domain *d, unsigned int virq, | 
					
						
							|  |  |  | 			    irq_hw_number_t hw) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct byt_gpio *vg = d->host_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	irq_set_chip_and_handler_name(virq, &byt_irqchip, handle_simple_irq, | 
					
						
							|  |  |  | 				      "demux"); | 
					
						
							|  |  |  | 	irq_set_chip_data(virq, vg); | 
					
						
							|  |  |  | 	irq_set_irq_type(virq, IRQ_TYPE_NONE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct irq_domain_ops byt_gpio_irq_ops = { | 
					
						
							|  |  |  | 	.map = byt_gpio_irq_map, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int byt_gpio_probe(struct platform_device *pdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct byt_gpio *vg; | 
					
						
							|  |  |  | 	struct gpio_chip *gc; | 
					
						
							|  |  |  | 	struct resource *mem_rc, *irq_rc; | 
					
						
							|  |  |  | 	struct device *dev = &pdev->dev; | 
					
						
							|  |  |  | 	struct acpi_device *acpi_dev; | 
					
						
							|  |  |  | 	struct pinctrl_gpio_range *range; | 
					
						
							|  |  |  | 	acpi_handle handle = ACPI_HANDLE(dev); | 
					
						
							|  |  |  | 	unsigned hwirq; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (acpi_bus_get_device(handle, &acpi_dev)) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vg = devm_kzalloc(dev, sizeof(struct byt_gpio), GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!vg) { | 
					
						
							|  |  |  | 		dev_err(&pdev->dev, "can't allocate byt_gpio chip data\n"); | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (range = byt_ranges; range->name; range++) { | 
					
						
							|  |  |  | 		if (!strcmp(acpi_dev->pnp.unique_id, range->name)) { | 
					
						
							|  |  |  | 			vg->chip.ngpio = range->npins; | 
					
						
							|  |  |  | 			vg->range = range; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!vg->chip.ngpio || !vg->range) | 
					
						
							|  |  |  | 		return -ENODEV; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vg->pdev = pdev; | 
					
						
							|  |  |  | 	platform_set_drvdata(pdev, vg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	mem_rc = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 
					
						
							|  |  |  | 	vg->reg_base = devm_ioremap_resource(dev, mem_rc); | 
					
						
							|  |  |  | 	if (IS_ERR(vg->reg_base)) | 
					
						
							|  |  |  | 		return PTR_ERR(vg->reg_base); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spin_lock_init(&vg->lock); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	gc = &vg->chip; | 
					
						
							|  |  |  | 	gc->label = dev_name(&pdev->dev); | 
					
						
							|  |  |  | 	gc->owner = THIS_MODULE; | 
					
						
							|  |  |  | 	gc->request = byt_gpio_request; | 
					
						
							|  |  |  | 	gc->free = byt_gpio_free; | 
					
						
							|  |  |  | 	gc->direction_input = byt_gpio_direction_input; | 
					
						
							|  |  |  | 	gc->direction_output = byt_gpio_direction_output; | 
					
						
							|  |  |  | 	gc->get = byt_gpio_get; | 
					
						
							|  |  |  | 	gc->set = byt_gpio_set; | 
					
						
							|  |  |  | 	gc->dbg_show = byt_gpio_dbg_show; | 
					
						
							|  |  |  | 	gc->base = -1; | 
					
						
							|  |  |  | 	gc->can_sleep = 0; | 
					
						
							|  |  |  | 	gc->dev = dev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = gpiochip_add(gc); | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							|  |  |  | 		dev_err(&pdev->dev, "failed adding byt-gpio chip\n"); | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* set up interrupts  */ | 
					
						
							|  |  |  | 	irq_rc = platform_get_resource(pdev, IORESOURCE_IRQ, 0); | 
					
						
							|  |  |  | 	if (irq_rc && irq_rc->start) { | 
					
						
							|  |  |  | 		hwirq = irq_rc->start; | 
					
						
							|  |  |  | 		gc->to_irq = byt_gpio_to_irq; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		vg->domain = irq_domain_add_linear(NULL, gc->ngpio, | 
					
						
							|  |  |  | 						   &byt_gpio_irq_ops, vg); | 
					
						
							|  |  |  | 		if (!vg->domain) | 
					
						
							|  |  |  | 			return -ENXIO; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		byt_gpio_irq_init_hw(vg); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		irq_set_handler_data(hwirq, vg); | 
					
						
							|  |  |  | 		irq_set_chained_handler(hwirq, byt_gpio_irq_handler); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Register interrupt handlers for gpio signaled acpi events */ | 
					
						
							|  |  |  | 		acpi_gpiochip_request_interrupts(gc); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	pm_runtime_enable(dev); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int byt_gpio_runtime_suspend(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int byt_gpio_runtime_resume(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct dev_pm_ops byt_gpio_pm_ops = { | 
					
						
							|  |  |  | 	.runtime_suspend = byt_gpio_runtime_suspend, | 
					
						
							|  |  |  | 	.runtime_resume = byt_gpio_runtime_resume, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct acpi_device_id byt_gpio_acpi_match[] = { | 
					
						
							|  |  |  | 	{ "INT33B2", 0 }, | 
					
						
							|  |  |  | 	{ } | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | MODULE_DEVICE_TABLE(acpi, byt_gpio_acpi_match); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int byt_gpio_remove(struct platform_device *pdev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct byt_gpio *vg = platform_get_drvdata(pdev); | 
					
						
							|  |  |  | 	int err; | 
					
						
							| 
									
										
										
										
											2013-07-10 14:55:36 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-18 14:33:02 +03:00
										 |  |  | 	pm_runtime_disable(&pdev->dev); | 
					
						
							|  |  |  | 	err = gpiochip_remove(&vg->chip); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		dev_warn(&pdev->dev, "failed to remove gpio_chip.\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static struct platform_driver byt_gpio_driver = { | 
					
						
							|  |  |  | 	.probe          = byt_gpio_probe, | 
					
						
							|  |  |  | 	.remove         = byt_gpio_remove, | 
					
						
							|  |  |  | 	.driver         = { | 
					
						
							|  |  |  | 		.name   = "byt_gpio", | 
					
						
							|  |  |  | 		.owner  = THIS_MODULE, | 
					
						
							|  |  |  | 		.pm	= &byt_gpio_pm_ops, | 
					
						
							|  |  |  | 		.acpi_match_table = ACPI_PTR(byt_gpio_acpi_match), | 
					
						
							|  |  |  | 	}, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int __init byt_gpio_init(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return platform_driver_register(&byt_gpio_driver); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | subsys_initcall(byt_gpio_init); |