1d01f8633SDamien Riegel /* 2d01f8633SDamien Riegel * Multiplexed-IRQs driver for TS-4800's FPGA 3d01f8633SDamien Riegel * 4d01f8633SDamien Riegel * Copyright (c) 2015 - Savoir-faire Linux 5d01f8633SDamien Riegel * 6d01f8633SDamien Riegel * This file is licensed under the terms of the GNU General Public 7d01f8633SDamien Riegel * License version 2. This program is licensed "as is" without any 8d01f8633SDamien Riegel * warranty of any kind, whether express or implied. 9d01f8633SDamien Riegel */ 10d01f8633SDamien Riegel 11d01f8633SDamien Riegel #include <linux/interrupt.h> 12d01f8633SDamien Riegel #include <linux/io.h> 13d01f8633SDamien Riegel #include <linux/irq.h> 14d01f8633SDamien Riegel #include <linux/irqchip.h> 15d01f8633SDamien Riegel #include <linux/irqchip/chained_irq.h> 16d01f8633SDamien Riegel #include <linux/irqdomain.h> 17d01f8633SDamien Riegel #include <linux/module.h> 18d01f8633SDamien Riegel #include <linux/of.h> 19d01f8633SDamien Riegel #include <linux/of_address.h> 20d01f8633SDamien Riegel #include <linux/of_irq.h> 21d01f8633SDamien Riegel #include <linux/platform_device.h> 223344265aSMarc Zyngier #include <linux/seq_file.h> 23d01f8633SDamien Riegel 24d01f8633SDamien Riegel #define IRQ_MASK 0x4 25d01f8633SDamien Riegel #define IRQ_STATUS 0x8 26d01f8633SDamien Riegel 27d01f8633SDamien Riegel struct ts4800_irq_data { 28d01f8633SDamien Riegel void __iomem *base; 293344265aSMarc Zyngier struct platform_device *pdev; 30d01f8633SDamien Riegel struct irq_domain *domain; 31d01f8633SDamien Riegel }; 32d01f8633SDamien Riegel 33d01f8633SDamien Riegel static void ts4800_irq_mask(struct irq_data *d) 34d01f8633SDamien Riegel { 35d01f8633SDamien Riegel struct ts4800_irq_data *data = irq_data_get_irq_chip_data(d); 36d01f8633SDamien Riegel u16 reg = readw(data->base + IRQ_MASK); 37d01f8633SDamien Riegel u16 mask = 1 << d->hwirq; 38d01f8633SDamien Riegel 39d01f8633SDamien Riegel writew(reg | mask, data->base + IRQ_MASK); 40d01f8633SDamien Riegel } 41d01f8633SDamien Riegel 42d01f8633SDamien Riegel static void ts4800_irq_unmask(struct irq_data *d) 43d01f8633SDamien Riegel { 44d01f8633SDamien Riegel struct ts4800_irq_data *data = irq_data_get_irq_chip_data(d); 45d01f8633SDamien Riegel u16 reg = readw(data->base + IRQ_MASK); 46d01f8633SDamien Riegel u16 mask = 1 << d->hwirq; 47d01f8633SDamien Riegel 48d01f8633SDamien Riegel writew(reg & ~mask, data->base + IRQ_MASK); 49d01f8633SDamien Riegel } 50d01f8633SDamien Riegel 513344265aSMarc Zyngier static void ts4800_irq_print_chip(struct irq_data *d, struct seq_file *p) 523344265aSMarc Zyngier { 533344265aSMarc Zyngier struct ts4800_irq_data *data = irq_data_get_irq_chip_data(d); 543344265aSMarc Zyngier 553344265aSMarc Zyngier seq_printf(p, "%s", dev_name(&data->pdev->dev)); 563344265aSMarc Zyngier } 573344265aSMarc Zyngier 583344265aSMarc Zyngier static const struct irq_chip ts4800_chip = { 593344265aSMarc Zyngier .irq_mask = ts4800_irq_mask, 603344265aSMarc Zyngier .irq_unmask = ts4800_irq_unmask, 613344265aSMarc Zyngier .irq_print_chip = ts4800_irq_print_chip, 623344265aSMarc Zyngier }; 633344265aSMarc Zyngier 64d01f8633SDamien Riegel static int ts4800_irqdomain_map(struct irq_domain *d, unsigned int irq, 65d01f8633SDamien Riegel irq_hw_number_t hwirq) 66d01f8633SDamien Riegel { 67d01f8633SDamien Riegel struct ts4800_irq_data *data = d->host_data; 68d01f8633SDamien Riegel 693344265aSMarc Zyngier irq_set_chip_and_handler(irq, &ts4800_chip, handle_simple_irq); 70d01f8633SDamien Riegel irq_set_chip_data(irq, data); 71d01f8633SDamien Riegel irq_set_noprobe(irq); 72d01f8633SDamien Riegel 73d01f8633SDamien Riegel return 0; 74d01f8633SDamien Riegel } 75d01f8633SDamien Riegel 76e4e1c0eaSAxel Lin static const struct irq_domain_ops ts4800_ic_ops = { 77d01f8633SDamien Riegel .map = ts4800_irqdomain_map, 78d01f8633SDamien Riegel .xlate = irq_domain_xlate_onecell, 79d01f8633SDamien Riegel }; 80d01f8633SDamien Riegel 81d01f8633SDamien Riegel static void ts4800_ic_chained_handle_irq(struct irq_desc *desc) 82d01f8633SDamien Riegel { 83d01f8633SDamien Riegel struct ts4800_irq_data *data = irq_desc_get_handler_data(desc); 84d01f8633SDamien Riegel struct irq_chip *chip = irq_desc_get_chip(desc); 85d01f8633SDamien Riegel u16 status = readw(data->base + IRQ_STATUS); 86d01f8633SDamien Riegel 87d01f8633SDamien Riegel chained_irq_enter(chip, desc); 88d01f8633SDamien Riegel 89d01f8633SDamien Riegel if (unlikely(status == 0)) { 90d01f8633SDamien Riegel handle_bad_irq(desc); 91d01f8633SDamien Riegel goto out; 92d01f8633SDamien Riegel } 93d01f8633SDamien Riegel 94d01f8633SDamien Riegel do { 95d01f8633SDamien Riegel unsigned int bit = __ffs(status); 96d01f8633SDamien Riegel 97046a6ee2SMarc Zyngier generic_handle_domain_irq(data->domain, bit); 98d01f8633SDamien Riegel status &= ~(1 << bit); 99d01f8633SDamien Riegel } while (status); 100d01f8633SDamien Riegel 101d01f8633SDamien Riegel out: 102d01f8633SDamien Riegel chained_irq_exit(chip, desc); 103d01f8633SDamien Riegel } 104d01f8633SDamien Riegel 105d01f8633SDamien Riegel static int ts4800_ic_probe(struct platform_device *pdev) 106d01f8633SDamien Riegel { 107d01f8633SDamien Riegel struct device_node *node = pdev->dev.of_node; 108d01f8633SDamien Riegel struct ts4800_irq_data *data; 109d01f8633SDamien Riegel int parent_irq; 110d01f8633SDamien Riegel 111d01f8633SDamien Riegel data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 112d01f8633SDamien Riegel if (!data) 113d01f8633SDamien Riegel return -ENOMEM; 114d01f8633SDamien Riegel 1153344265aSMarc Zyngier data->pdev = pdev; 1162687bf8dSCai Huoqing data->base = devm_platform_ioremap_resource(pdev, 0); 117d01f8633SDamien Riegel if (IS_ERR(data->base)) 118d01f8633SDamien Riegel return PTR_ERR(data->base); 119d01f8633SDamien Riegel 120d01f8633SDamien Riegel writew(0xFFFF, data->base + IRQ_MASK); 121d01f8633SDamien Riegel 122d01f8633SDamien Riegel parent_irq = irq_of_parse_and_map(node, 0); 123d01f8633SDamien Riegel if (!parent_irq) { 124d01f8633SDamien Riegel dev_err(&pdev->dev, "failed to get parent IRQ\n"); 125d01f8633SDamien Riegel return -EINVAL; 126d01f8633SDamien Riegel } 127d01f8633SDamien Riegel 128d01f8633SDamien Riegel data->domain = irq_domain_add_linear(node, 8, &ts4800_ic_ops, data); 129d01f8633SDamien Riegel if (!data->domain) { 130d01f8633SDamien Riegel dev_err(&pdev->dev, "cannot add IRQ domain\n"); 131d01f8633SDamien Riegel return -ENOMEM; 132d01f8633SDamien Riegel } 133d01f8633SDamien Riegel 134d01f8633SDamien Riegel irq_set_chained_handler_and_data(parent_irq, 135d01f8633SDamien Riegel ts4800_ic_chained_handle_irq, data); 136d01f8633SDamien Riegel 137d01f8633SDamien Riegel platform_set_drvdata(pdev, data); 138d01f8633SDamien Riegel 139d01f8633SDamien Riegel return 0; 140d01f8633SDamien Riegel } 141d01f8633SDamien Riegel 142d01f8633SDamien Riegel static int ts4800_ic_remove(struct platform_device *pdev) 143d01f8633SDamien Riegel { 144d01f8633SDamien Riegel struct ts4800_irq_data *data = platform_get_drvdata(pdev); 145d01f8633SDamien Riegel 146d01f8633SDamien Riegel irq_domain_remove(data->domain); 147d01f8633SDamien Riegel 148d01f8633SDamien Riegel return 0; 149d01f8633SDamien Riegel } 150d01f8633SDamien Riegel 151d01f8633SDamien Riegel static const struct of_device_id ts4800_ic_of_match[] = { 152d01f8633SDamien Riegel { .compatible = "technologic,ts4800-irqc", }, 153d01f8633SDamien Riegel {}, 154d01f8633SDamien Riegel }; 155d01f8633SDamien Riegel MODULE_DEVICE_TABLE(of, ts4800_ic_of_match); 156d01f8633SDamien Riegel 157d01f8633SDamien Riegel static struct platform_driver ts4800_ic_driver = { 158d01f8633SDamien Riegel .probe = ts4800_ic_probe, 159d01f8633SDamien Riegel .remove = ts4800_ic_remove, 160d01f8633SDamien Riegel .driver = { 161d01f8633SDamien Riegel .name = "ts4800-irqc", 162d01f8633SDamien Riegel .of_match_table = ts4800_ic_of_match, 163d01f8633SDamien Riegel }, 164d01f8633SDamien Riegel }; 165d01f8633SDamien Riegel module_platform_driver(ts4800_ic_driver); 166d01f8633SDamien Riegel 167d01f8633SDamien Riegel MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>"); 168d01f8633SDamien Riegel MODULE_LICENSE("GPL v2"); 169d01f8633SDamien Riegel MODULE_ALIAS("platform:ts4800_irqc"); 170