brintos

brintos / linux-shallow public Read only

0
0
Text · 11.2 KiB · a211a02 Raw
443 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2008, 2009 Provigent Ltd.4 *5 * Author: Baruch Siach <baruch@tkos.co.il>6 *7 * Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061)8 *9 * Data sheet: ARM DDI 0190B, September 200010 */11#include <linux/amba/bus.h>12#include <linux/bitops.h>13#include <linux/device.h>14#include <linux/errno.h>15#include <linux/gpio/driver.h>16#include <linux/init.h>17#include <linux/interrupt.h>18#include <linux/io.h>19#include <linux/ioport.h>20#include <linux/irq.h>21#include <linux/irqchip/chained_irq.h>22#include <linux/module.h>23#include <linux/pinctrl/consumer.h>24#include <linux/pm.h>25#include <linux/seq_file.h>26#include <linux/slab.h>27#include <linux/spinlock.h>28 29#define GPIODIR 0x40030#define GPIOIS  0x40431#define GPIOIBE 0x40832#define GPIOIEV 0x40C33#define GPIOIE  0x41034#define GPIORIS 0x41435#define GPIOMIS 0x41836#define GPIOIC  0x41C37 38#define PL061_GPIO_NR	839 40#ifdef CONFIG_PM41struct pl061_context_save_regs {42	u8 gpio_data;43	u8 gpio_dir;44	u8 gpio_is;45	u8 gpio_ibe;46	u8 gpio_iev;47	u8 gpio_ie;48};49#endif50 51struct pl061 {52	raw_spinlock_t		lock;53 54	void __iomem		*base;55	struct gpio_chip	gc;56	int			parent_irq;57 58#ifdef CONFIG_PM59	struct pl061_context_save_regs csave_regs;60#endif61};62 63static int pl061_get_direction(struct gpio_chip *gc, unsigned offset)64{65	struct pl061 *pl061 = gpiochip_get_data(gc);66 67	if (readb(pl061->base + GPIODIR) & BIT(offset))68		return GPIO_LINE_DIRECTION_OUT;69 70	return GPIO_LINE_DIRECTION_IN;71}72 73static int pl061_direction_input(struct gpio_chip *gc, unsigned offset)74{75	struct pl061 *pl061 = gpiochip_get_data(gc);76	unsigned long flags;77	unsigned char gpiodir;78 79	raw_spin_lock_irqsave(&pl061->lock, flags);80	gpiodir = readb(pl061->base + GPIODIR);81	gpiodir &= ~(BIT(offset));82	writeb(gpiodir, pl061->base + GPIODIR);83	raw_spin_unlock_irqrestore(&pl061->lock, flags);84 85	return 0;86}87 88static int pl061_direction_output(struct gpio_chip *gc, unsigned offset,89		int value)90{91	struct pl061 *pl061 = gpiochip_get_data(gc);92	unsigned long flags;93	unsigned char gpiodir;94 95	raw_spin_lock_irqsave(&pl061->lock, flags);96	writeb(!!value << offset, pl061->base + (BIT(offset + 2)));97	gpiodir = readb(pl061->base + GPIODIR);98	gpiodir |= BIT(offset);99	writeb(gpiodir, pl061->base + GPIODIR);100 101	/*102	 * gpio value is set again, because pl061 doesn't allow to set value of103	 * a gpio pin before configuring it in OUT mode.104	 */105	writeb(!!value << offset, pl061->base + (BIT(offset + 2)));106	raw_spin_unlock_irqrestore(&pl061->lock, flags);107 108	return 0;109}110 111static int pl061_get_value(struct gpio_chip *gc, unsigned offset)112{113	struct pl061 *pl061 = gpiochip_get_data(gc);114 115	return !!readb(pl061->base + (BIT(offset + 2)));116}117 118static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value)119{120	struct pl061 *pl061 = gpiochip_get_data(gc);121 122	writeb(!!value << offset, pl061->base + (BIT(offset + 2)));123}124 125static int pl061_irq_type(struct irq_data *d, unsigned trigger)126{127	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);128	struct pl061 *pl061 = gpiochip_get_data(gc);129	int offset = irqd_to_hwirq(d);130	unsigned long flags;131	u8 gpiois, gpioibe, gpioiev;132	u8 bit = BIT(offset);133 134	if (offset < 0 || offset >= PL061_GPIO_NR)135		return -EINVAL;136 137	if ((trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) &&138	    (trigger & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING)))139	{140		dev_err(gc->parent,141			"trying to configure line %d for both level and edge "142			"detection, choose one!\n",143			offset);144		return -EINVAL;145	}146 147 148	raw_spin_lock_irqsave(&pl061->lock, flags);149 150	gpioiev = readb(pl061->base + GPIOIEV);151	gpiois = readb(pl061->base + GPIOIS);152	gpioibe = readb(pl061->base + GPIOIBE);153 154	if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {155		bool polarity = trigger & IRQ_TYPE_LEVEL_HIGH;156 157		/* Disable edge detection */158		gpioibe &= ~bit;159		/* Enable level detection */160		gpiois |= bit;161		/* Select polarity */162		if (polarity)163			gpioiev |= bit;164		else165			gpioiev &= ~bit;166		irq_set_handler_locked(d, handle_level_irq);167		dev_dbg(gc->parent, "line %d: IRQ on %s level\n",168			offset,169			polarity ? "HIGH" : "LOW");170	} else if ((trigger & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) {171		/* Disable level detection */172		gpiois &= ~bit;173		/* Select both edges, setting this makes GPIOEV be ignored */174		gpioibe |= bit;175		irq_set_handler_locked(d, handle_edge_irq);176		dev_dbg(gc->parent, "line %d: IRQ on both edges\n", offset);177	} else if ((trigger & IRQ_TYPE_EDGE_RISING) ||178		   (trigger & IRQ_TYPE_EDGE_FALLING)) {179		bool rising = trigger & IRQ_TYPE_EDGE_RISING;180 181		/* Disable level detection */182		gpiois &= ~bit;183		/* Clear detection on both edges */184		gpioibe &= ~bit;185		/* Select edge */186		if (rising)187			gpioiev |= bit;188		else189			gpioiev &= ~bit;190		irq_set_handler_locked(d, handle_edge_irq);191		dev_dbg(gc->parent, "line %d: IRQ on %s edge\n",192			offset,193			rising ? "RISING" : "FALLING");194	} else {195		/* No trigger: disable everything */196		gpiois &= ~bit;197		gpioibe &= ~bit;198		gpioiev &= ~bit;199		irq_set_handler_locked(d, handle_bad_irq);200		dev_warn(gc->parent, "no trigger selected for line %d\n",201			 offset);202	}203 204	writeb(gpiois, pl061->base + GPIOIS);205	writeb(gpioibe, pl061->base + GPIOIBE);206	writeb(gpioiev, pl061->base + GPIOIEV);207 208	raw_spin_unlock_irqrestore(&pl061->lock, flags);209 210	return 0;211}212 213static void pl061_irq_handler(struct irq_desc *desc)214{215	unsigned long pending;216	int offset;217	struct gpio_chip *gc = irq_desc_get_handler_data(desc);218	struct pl061 *pl061 = gpiochip_get_data(gc);219	struct irq_chip *irqchip = irq_desc_get_chip(desc);220 221	chained_irq_enter(irqchip, desc);222 223	pending = readb(pl061->base + GPIOMIS);224	if (pending) {225		for_each_set_bit(offset, &pending, PL061_GPIO_NR)226			generic_handle_domain_irq(gc->irq.domain,227						  offset);228	}229 230	chained_irq_exit(irqchip, desc);231}232 233static void pl061_irq_mask(struct irq_data *d)234{235	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);236	struct pl061 *pl061 = gpiochip_get_data(gc);237	u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);238	u8 gpioie;239 240	raw_spin_lock(&pl061->lock);241	gpioie = readb(pl061->base + GPIOIE) & ~mask;242	writeb(gpioie, pl061->base + GPIOIE);243	raw_spin_unlock(&pl061->lock);244 245	gpiochip_disable_irq(gc, d->hwirq);246}247 248static void pl061_irq_unmask(struct irq_data *d)249{250	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);251	struct pl061 *pl061 = gpiochip_get_data(gc);252	u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);253	u8 gpioie;254 255	gpiochip_enable_irq(gc, d->hwirq);256 257	raw_spin_lock(&pl061->lock);258	gpioie = readb(pl061->base + GPIOIE) | mask;259	writeb(gpioie, pl061->base + GPIOIE);260	raw_spin_unlock(&pl061->lock);261}262 263/**264 * pl061_irq_ack() - ACK an edge IRQ265 * @d: IRQ data for this IRQ266 *267 * This gets called from the edge IRQ handler to ACK the edge IRQ268 * in the GPIOIC (interrupt-clear) register. For level IRQs this is269 * not needed: these go away when the level signal goes away.270 */271static void pl061_irq_ack(struct irq_data *d)272{273	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);274	struct pl061 *pl061 = gpiochip_get_data(gc);275	u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);276 277	raw_spin_lock(&pl061->lock);278	writeb(mask, pl061->base + GPIOIC);279	raw_spin_unlock(&pl061->lock);280}281 282static int pl061_irq_set_wake(struct irq_data *d, unsigned int state)283{284	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);285	struct pl061 *pl061 = gpiochip_get_data(gc);286 287	return irq_set_irq_wake(pl061->parent_irq, state);288}289 290static void pl061_irq_print_chip(struct irq_data *data, struct seq_file *p)291{292	struct gpio_chip *gc = irq_data_get_irq_chip_data(data);293 294	seq_printf(p, dev_name(gc->parent));295}296 297static const struct irq_chip pl061_irq_chip = {298	.irq_ack		= pl061_irq_ack,299	.irq_mask		= pl061_irq_mask,300	.irq_unmask		= pl061_irq_unmask,301	.irq_set_type		= pl061_irq_type,302	.irq_set_wake		= pl061_irq_set_wake,303	.irq_print_chip		= pl061_irq_print_chip,304	.flags			= IRQCHIP_IMMUTABLE,305	GPIOCHIP_IRQ_RESOURCE_HELPERS,306};307 308static int pl061_probe(struct amba_device *adev, const struct amba_id *id)309{310	struct device *dev = &adev->dev;311	struct pl061 *pl061;312	struct gpio_irq_chip *girq;313	int ret, irq;314 315	pl061 = devm_kzalloc(dev, sizeof(*pl061), GFP_KERNEL);316	if (pl061 == NULL)317		return -ENOMEM;318 319	pl061->base = devm_ioremap_resource(dev, &adev->res);320	if (IS_ERR(pl061->base))321		return PTR_ERR(pl061->base);322 323	raw_spin_lock_init(&pl061->lock);324	pl061->gc.request = gpiochip_generic_request;325	pl061->gc.free = gpiochip_generic_free;326	pl061->gc.base = -1;327	pl061->gc.get_direction = pl061_get_direction;328	pl061->gc.direction_input = pl061_direction_input;329	pl061->gc.direction_output = pl061_direction_output;330	pl061->gc.get = pl061_get_value;331	pl061->gc.set = pl061_set_value;332	pl061->gc.ngpio = PL061_GPIO_NR;333	pl061->gc.label = dev_name(dev);334	pl061->gc.parent = dev;335	pl061->gc.owner = THIS_MODULE;336 337	/*338	 * irq_chip support339	 */340	writeb(0, pl061->base + GPIOIE); /* disable irqs */341	irq = adev->irq[0];342	if (!irq)343		dev_warn(&adev->dev, "IRQ support disabled\n");344	pl061->parent_irq = irq;345 346	girq = &pl061->gc.irq;347	gpio_irq_chip_set_chip(girq, &pl061_irq_chip);348	girq->parent_handler = pl061_irq_handler;349	girq->num_parents = 1;350	girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents),351				     GFP_KERNEL);352	if (!girq->parents)353		return -ENOMEM;354	girq->parents[0] = irq;355	girq->default_type = IRQ_TYPE_NONE;356	girq->handler = handle_bad_irq;357 358	ret = devm_gpiochip_add_data(dev, &pl061->gc, pl061);359	if (ret)360		return ret;361 362	amba_set_drvdata(adev, pl061);363	dev_info(dev, "PL061 GPIO chip registered\n");364 365	return 0;366}367 368#ifdef CONFIG_PM369static int pl061_suspend(struct device *dev)370{371	struct pl061 *pl061 = dev_get_drvdata(dev);372	int offset;373 374	pl061->csave_regs.gpio_data = 0;375	pl061->csave_regs.gpio_dir = readb(pl061->base + GPIODIR);376	pl061->csave_regs.gpio_is = readb(pl061->base + GPIOIS);377	pl061->csave_regs.gpio_ibe = readb(pl061->base + GPIOIBE);378	pl061->csave_regs.gpio_iev = readb(pl061->base + GPIOIEV);379	pl061->csave_regs.gpio_ie = readb(pl061->base + GPIOIE);380 381	for (offset = 0; offset < PL061_GPIO_NR; offset++) {382		if (pl061->csave_regs.gpio_dir & (BIT(offset)))383			pl061->csave_regs.gpio_data |=384				pl061_get_value(&pl061->gc, offset) << offset;385	}386 387	return 0;388}389 390static int pl061_resume(struct device *dev)391{392	struct pl061 *pl061 = dev_get_drvdata(dev);393	int offset;394 395	for (offset = 0; offset < PL061_GPIO_NR; offset++) {396		if (pl061->csave_regs.gpio_dir & (BIT(offset)))397			pl061_direction_output(&pl061->gc, offset,398					pl061->csave_regs.gpio_data &399					(BIT(offset)));400		else401			pl061_direction_input(&pl061->gc, offset);402	}403 404	writeb(pl061->csave_regs.gpio_is, pl061->base + GPIOIS);405	writeb(pl061->csave_regs.gpio_ibe, pl061->base + GPIOIBE);406	writeb(pl061->csave_regs.gpio_iev, pl061->base + GPIOIEV);407	writeb(pl061->csave_regs.gpio_ie, pl061->base + GPIOIE);408 409	return 0;410}411 412static const struct dev_pm_ops pl061_dev_pm_ops = {413	.suspend = pl061_suspend,414	.resume = pl061_resume,415	.freeze = pl061_suspend,416	.restore = pl061_resume,417};418#endif419 420static const struct amba_id pl061_ids[] = {421	{422		.id	= 0x00041061,423		.mask	= 0x000fffff,424	},425	{ 0, 0 },426};427MODULE_DEVICE_TABLE(amba, pl061_ids);428 429static struct amba_driver pl061_gpio_driver = {430	.drv = {431		.name	= "pl061_gpio",432#ifdef CONFIG_PM433		.pm	= &pl061_dev_pm_ops,434#endif435	},436	.id_table	= pl061_ids,437	.probe		= pl061_probe,438};439module_amba_driver(pl061_gpio_driver);440 441MODULE_DESCRIPTION("Driver for the ARM PrimeCell(tm) General Purpose Input/Output (PL061)");442MODULE_LICENSE("GPL v2");443