brintos

brintos / linux-shallow public Read only

0
0
Text · 9.6 KiB · c2edfbb Raw
357 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright (C) 2013 Altera Corporation4 * Based on gpio-mpc8xxx.c5 */6 7#include <linux/io.h>8#include <linux/module.h>9#include <linux/gpio/driver.h>10#include <linux/gpio/legacy-of-mm-gpiochip.h>11#include <linux/platform_device.h>12 13#define ALTERA_GPIO_MAX_NGPIO		3214#define ALTERA_GPIO_DATA		0x015#define ALTERA_GPIO_DIR			0x416#define ALTERA_GPIO_IRQ_MASK		0x817#define ALTERA_GPIO_EDGE_CAP		0xc18 19/**20* struct altera_gpio_chip21* @mmchip		: memory mapped chip structure.22* @gpio_lock		: synchronization lock so that new irq/set/get requests23*			  will be blocked until the current one completes.24* @interrupt_trigger	: specifies the hardware configured IRQ trigger type25*			  (rising, falling, both, high)26* @mapped_irq		: kernel mapped irq number.27*/28struct altera_gpio_chip {29	struct of_mm_gpio_chip mmchip;30	raw_spinlock_t gpio_lock;31	int interrupt_trigger;32	int mapped_irq;33};34 35static void altera_gpio_irq_unmask(struct irq_data *d)36{37	struct altera_gpio_chip *altera_gc;38	struct of_mm_gpio_chip *mm_gc;39	unsigned long flags;40	u32 intmask;41 42	altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d));43	mm_gc = &altera_gc->mmchip;44	gpiochip_enable_irq(&mm_gc->gc, irqd_to_hwirq(d));45 46	raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags);47	intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK);48	/* Set ALTERA_GPIO_IRQ_MASK bit to unmask */49	intmask |= BIT(irqd_to_hwirq(d));50	writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK);51	raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags);52}53 54static void altera_gpio_irq_mask(struct irq_data *d)55{56	struct altera_gpio_chip *altera_gc;57	struct of_mm_gpio_chip *mm_gc;58	unsigned long flags;59	u32 intmask;60 61	altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d));62	mm_gc = &altera_gc->mmchip;63 64	raw_spin_lock_irqsave(&altera_gc->gpio_lock, flags);65	intmask = readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK);66	/* Clear ALTERA_GPIO_IRQ_MASK bit to mask */67	intmask &= ~BIT(irqd_to_hwirq(d));68	writel(intmask, mm_gc->regs + ALTERA_GPIO_IRQ_MASK);69	raw_spin_unlock_irqrestore(&altera_gc->gpio_lock, flags);70	gpiochip_disable_irq(&mm_gc->gc, irqd_to_hwirq(d));71}72 73/*74 * This controller's IRQ type is synthesized in hardware, so this function75 * just checks if the requested set_type matches the synthesized IRQ type76 */77static int altera_gpio_irq_set_type(struct irq_data *d,78				   unsigned int type)79{80	struct altera_gpio_chip *altera_gc;81 82	altera_gc = gpiochip_get_data(irq_data_get_irq_chip_data(d));83 84	if (type == IRQ_TYPE_NONE) {85		irq_set_handler_locked(d, handle_bad_irq);86		return 0;87	}88	if (type == altera_gc->interrupt_trigger) {89		if (type == IRQ_TYPE_LEVEL_HIGH)90			irq_set_handler_locked(d, handle_level_irq);91		else92			irq_set_handler_locked(d, handle_simple_irq);93		return 0;94	}95	irq_set_handler_locked(d, handle_bad_irq);96	return -EINVAL;97}98 99static unsigned int altera_gpio_irq_startup(struct irq_data *d)100{101	altera_gpio_irq_unmask(d);102 103	return 0;104}105 106static int altera_gpio_get(struct gpio_chip *gc, unsigned offset)107{108	struct of_mm_gpio_chip *mm_gc;109 110	mm_gc = to_of_mm_gpio_chip(gc);111 112	return !!(readl(mm_gc->regs + ALTERA_GPIO_DATA) & BIT(offset));113}114 115static void altera_gpio_set(struct gpio_chip *gc, unsigned offset, int value)116{117	struct of_mm_gpio_chip *mm_gc;118	struct altera_gpio_chip *chip;119	unsigned long flags;120	unsigned int data_reg;121 122	mm_gc = to_of_mm_gpio_chip(gc);123	chip = gpiochip_get_data(gc);124 125	raw_spin_lock_irqsave(&chip->gpio_lock, flags);126	data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA);127	if (value)128		data_reg |= BIT(offset);129	else130		data_reg &= ~BIT(offset);131	writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA);132	raw_spin_unlock_irqrestore(&chip->gpio_lock, flags);133}134 135static int altera_gpio_direction_input(struct gpio_chip *gc, unsigned offset)136{137	struct of_mm_gpio_chip *mm_gc;138	struct altera_gpio_chip *chip;139	unsigned long flags;140	unsigned int gpio_ddr;141 142	mm_gc = to_of_mm_gpio_chip(gc);143	chip = gpiochip_get_data(gc);144 145	raw_spin_lock_irqsave(&chip->gpio_lock, flags);146	/* Set pin as input, assumes software controlled IP */147	gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR);148	gpio_ddr &= ~BIT(offset);149	writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR);150	raw_spin_unlock_irqrestore(&chip->gpio_lock, flags);151 152	return 0;153}154 155static int altera_gpio_direction_output(struct gpio_chip *gc,156		unsigned offset, int value)157{158	struct of_mm_gpio_chip *mm_gc;159	struct altera_gpio_chip *chip;160	unsigned long flags;161	unsigned int data_reg, gpio_ddr;162 163	mm_gc = to_of_mm_gpio_chip(gc);164	chip = gpiochip_get_data(gc);165 166	raw_spin_lock_irqsave(&chip->gpio_lock, flags);167	/* Sets the GPIO value */168	data_reg = readl(mm_gc->regs + ALTERA_GPIO_DATA);169	if (value)170		data_reg |= BIT(offset);171	else172		data_reg &= ~BIT(offset);173	writel(data_reg, mm_gc->regs + ALTERA_GPIO_DATA);174 175	/* Set pin as output, assumes software controlled IP */176	gpio_ddr = readl(mm_gc->regs + ALTERA_GPIO_DIR);177	gpio_ddr |= BIT(offset);178	writel(gpio_ddr, mm_gc->regs + ALTERA_GPIO_DIR);179	raw_spin_unlock_irqrestore(&chip->gpio_lock, flags);180 181	return 0;182}183 184static void altera_gpio_irq_edge_handler(struct irq_desc *desc)185{186	struct altera_gpio_chip *altera_gc;187	struct irq_chip *chip;188	struct of_mm_gpio_chip *mm_gc;189	struct irq_domain *irqdomain;190	unsigned long status;191	int i;192 193	altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc));194	chip = irq_desc_get_chip(desc);195	mm_gc = &altera_gc->mmchip;196	irqdomain = altera_gc->mmchip.gc.irq.domain;197 198	chained_irq_enter(chip, desc);199 200	while ((status =201	      (readl(mm_gc->regs + ALTERA_GPIO_EDGE_CAP) &202	      readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK)))) {203		writel(status, mm_gc->regs + ALTERA_GPIO_EDGE_CAP);204		for_each_set_bit(i, &status, mm_gc->gc.ngpio)205			generic_handle_domain_irq(irqdomain, i);206	}207 208	chained_irq_exit(chip, desc);209}210 211static void altera_gpio_irq_leveL_high_handler(struct irq_desc *desc)212{213	struct altera_gpio_chip *altera_gc;214	struct irq_chip *chip;215	struct of_mm_gpio_chip *mm_gc;216	struct irq_domain *irqdomain;217	unsigned long status;218	int i;219 220	altera_gc = gpiochip_get_data(irq_desc_get_handler_data(desc));221	chip = irq_desc_get_chip(desc);222	mm_gc = &altera_gc->mmchip;223	irqdomain = altera_gc->mmchip.gc.irq.domain;224 225	chained_irq_enter(chip, desc);226 227	status = readl(mm_gc->regs + ALTERA_GPIO_DATA);228	status &= readl(mm_gc->regs + ALTERA_GPIO_IRQ_MASK);229 230	for_each_set_bit(i, &status, mm_gc->gc.ngpio)231		generic_handle_domain_irq(irqdomain, i);232 233	chained_irq_exit(chip, desc);234}235 236static const struct irq_chip altera_gpio_irq_chip = {237	.name = "altera-gpio",238	.irq_mask = altera_gpio_irq_mask,239	.irq_unmask = altera_gpio_irq_unmask,240	.irq_set_type = altera_gpio_irq_set_type,241	.irq_startup  = altera_gpio_irq_startup,242	.irq_shutdown = altera_gpio_irq_mask,243	.flags = IRQCHIP_IMMUTABLE,244	GPIOCHIP_IRQ_RESOURCE_HELPERS,245};246 247static int altera_gpio_probe(struct platform_device *pdev)248{249	struct device_node *node = pdev->dev.of_node;250	int reg, ret;251	struct altera_gpio_chip *altera_gc;252	struct gpio_irq_chip *girq;253 254	altera_gc = devm_kzalloc(&pdev->dev, sizeof(*altera_gc), GFP_KERNEL);255	if (!altera_gc)256		return -ENOMEM;257 258	raw_spin_lock_init(&altera_gc->gpio_lock);259 260	if (of_property_read_u32(node, "altr,ngpio", &reg))261		/* By default assume maximum ngpio */262		altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO;263	else264		altera_gc->mmchip.gc.ngpio = reg;265 266	if (altera_gc->mmchip.gc.ngpio > ALTERA_GPIO_MAX_NGPIO) {267		dev_warn(&pdev->dev,268			"ngpio is greater than %d, defaulting to %d\n",269			ALTERA_GPIO_MAX_NGPIO, ALTERA_GPIO_MAX_NGPIO);270		altera_gc->mmchip.gc.ngpio = ALTERA_GPIO_MAX_NGPIO;271	}272 273	altera_gc->mmchip.gc.direction_input	= altera_gpio_direction_input;274	altera_gc->mmchip.gc.direction_output	= altera_gpio_direction_output;275	altera_gc->mmchip.gc.get		= altera_gpio_get;276	altera_gc->mmchip.gc.set		= altera_gpio_set;277	altera_gc->mmchip.gc.owner		= THIS_MODULE;278	altera_gc->mmchip.gc.parent		= &pdev->dev;279 280	altera_gc->mapped_irq = platform_get_irq_optional(pdev, 0);281 282	if (altera_gc->mapped_irq < 0)283		goto skip_irq;284 285	if (of_property_read_u32(node, "altr,interrupt-type", &reg)) {286		dev_err(&pdev->dev,287			"altr,interrupt-type value not set in device tree\n");288		return -EINVAL;289	}290	altera_gc->interrupt_trigger = reg;291 292	girq = &altera_gc->mmchip.gc.irq;293	gpio_irq_chip_set_chip(girq, &altera_gpio_irq_chip);294 295	if (altera_gc->interrupt_trigger == IRQ_TYPE_LEVEL_HIGH)296		girq->parent_handler = altera_gpio_irq_leveL_high_handler;297	else298		girq->parent_handler = altera_gpio_irq_edge_handler;299	girq->num_parents = 1;300	girq->parents = devm_kcalloc(&pdev->dev, 1, sizeof(*girq->parents),301				     GFP_KERNEL);302	if (!girq->parents)303		return -ENOMEM;304	girq->default_type = IRQ_TYPE_NONE;305	girq->handler = handle_bad_irq;306	girq->parents[0] = altera_gc->mapped_irq;307 308skip_irq:309	ret = of_mm_gpiochip_add_data(node, &altera_gc->mmchip, altera_gc);310	if (ret) {311		dev_err(&pdev->dev, "Failed adding memory mapped gpiochip\n");312		return ret;313	}314 315	platform_set_drvdata(pdev, altera_gc);316 317	return 0;318}319 320static void altera_gpio_remove(struct platform_device *pdev)321{322	struct altera_gpio_chip *altera_gc = platform_get_drvdata(pdev);323 324	of_mm_gpiochip_remove(&altera_gc->mmchip);325}326 327static const struct of_device_id altera_gpio_of_match[] = {328	{ .compatible = "altr,pio-1.0", },329	{},330};331MODULE_DEVICE_TABLE(of, altera_gpio_of_match);332 333static struct platform_driver altera_gpio_driver = {334	.driver = {335		.name	= "altera_gpio",336		.of_match_table = altera_gpio_of_match,337	},338	.probe		= altera_gpio_probe,339	.remove_new	= altera_gpio_remove,340};341 342static int __init altera_gpio_init(void)343{344	return platform_driver_register(&altera_gpio_driver);345}346subsys_initcall(altera_gpio_init);347 348static void __exit altera_gpio_exit(void)349{350	platform_driver_unregister(&altera_gpio_driver);351}352module_exit(altera_gpio_exit);353 354MODULE_AUTHOR("Tien Hock Loh <thloh@altera.com>");355MODULE_DESCRIPTION("Altera GPIO driver");356MODULE_LICENSE("GPL");357