1b43ab901SSebastian Andrzej Siewior /* 2b43ab901SSebastian Andrzej Siewior * GPIO interface for Intel Sodaville SoCs. 3b43ab901SSebastian Andrzej Siewior * 4b43ab901SSebastian Andrzej Siewior * Copyright (c) 2010, 2011 Intel Corporation 5b43ab901SSebastian Andrzej Siewior * 6b43ab901SSebastian Andrzej Siewior * This program is free software; you can redistribute it and/or modify 7b43ab901SSebastian Andrzej Siewior * it under the terms of the GNU General Public License 2 as published 8b43ab901SSebastian Andrzej Siewior * by the Free Software Foundation. 9b43ab901SSebastian Andrzej Siewior * 10b43ab901SSebastian Andrzej Siewior */ 11b43ab901SSebastian Andrzej Siewior 12b43ab901SSebastian Andrzej Siewior #include <linux/errno.h> 13b43ab901SSebastian Andrzej Siewior #include <linux/gpio.h> 14b43ab901SSebastian Andrzej Siewior #include <linux/init.h> 15b43ab901SSebastian Andrzej Siewior #include <linux/io.h> 16b43ab901SSebastian Andrzej Siewior #include <linux/irq.h> 17b43ab901SSebastian Andrzej Siewior #include <linux/interrupt.h> 18b43ab901SSebastian Andrzej Siewior #include <linux/kernel.h> 19b43ab901SSebastian Andrzej Siewior #include <linux/module.h> 20b43ab901SSebastian Andrzej Siewior #include <linux/pci.h> 21b43ab901SSebastian Andrzej Siewior #include <linux/platform_device.h> 22b43ab901SSebastian Andrzej Siewior #include <linux/of_irq.h> 23b43ab901SSebastian Andrzej Siewior #include <linux/basic_mmio_gpio.h> 24b43ab901SSebastian Andrzej Siewior 25b43ab901SSebastian Andrzej Siewior #define DRV_NAME "sdv_gpio" 26b43ab901SSebastian Andrzej Siewior #define SDV_NUM_PUB_GPIOS 12 27b43ab901SSebastian Andrzej Siewior #define PCI_DEVICE_ID_SDV_GPIO 0x2e67 28b43ab901SSebastian Andrzej Siewior #define GPIO_BAR 0 29b43ab901SSebastian Andrzej Siewior 30b43ab901SSebastian Andrzej Siewior #define GPOUTR 0x00 31b43ab901SSebastian Andrzej Siewior #define GPOER 0x04 32b43ab901SSebastian Andrzej Siewior #define GPINR 0x08 33b43ab901SSebastian Andrzej Siewior 34b43ab901SSebastian Andrzej Siewior #define GPSTR 0x0c 35b43ab901SSebastian Andrzej Siewior #define GPIT1R0 0x10 36b43ab901SSebastian Andrzej Siewior #define GPIO_INT 0x14 37b43ab901SSebastian Andrzej Siewior #define GPIT1R1 0x18 38b43ab901SSebastian Andrzej Siewior 39b43ab901SSebastian Andrzej Siewior #define GPMUXCTL 0x1c 40b43ab901SSebastian Andrzej Siewior 41b43ab901SSebastian Andrzej Siewior struct sdv_gpio_chip_data { 42b43ab901SSebastian Andrzej Siewior int irq_base; 43b43ab901SSebastian Andrzej Siewior void __iomem *gpio_pub_base; 443ffc9cebSGrant Likely struct irq_domain *id; 45b43ab901SSebastian Andrzej Siewior struct irq_chip_generic *gc; 46b43ab901SSebastian Andrzej Siewior struct bgpio_chip bgpio; 47b43ab901SSebastian Andrzej Siewior }; 48b43ab901SSebastian Andrzej Siewior 49b43ab901SSebastian Andrzej Siewior static int sdv_gpio_pub_set_type(struct irq_data *d, unsigned int type) 50b43ab901SSebastian Andrzej Siewior { 51b43ab901SSebastian Andrzej Siewior struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 52b43ab901SSebastian Andrzej Siewior struct sdv_gpio_chip_data *sd = gc->private; 53b43ab901SSebastian Andrzej Siewior void __iomem *type_reg; 54b43ab901SSebastian Andrzej Siewior u32 reg; 55b43ab901SSebastian Andrzej Siewior 563ffc9cebSGrant Likely if (d->hwirq < 8) 57b43ab901SSebastian Andrzej Siewior type_reg = sd->gpio_pub_base + GPIT1R0; 58b43ab901SSebastian Andrzej Siewior else 59b43ab901SSebastian Andrzej Siewior type_reg = sd->gpio_pub_base + GPIT1R1; 60b43ab901SSebastian Andrzej Siewior 61b43ab901SSebastian Andrzej Siewior reg = readl(type_reg); 62b43ab901SSebastian Andrzej Siewior 63b43ab901SSebastian Andrzej Siewior switch (type) { 64b43ab901SSebastian Andrzej Siewior case IRQ_TYPE_LEVEL_HIGH: 653ffc9cebSGrant Likely reg &= ~BIT(4 * (d->hwirq % 8)); 66b43ab901SSebastian Andrzej Siewior break; 67b43ab901SSebastian Andrzej Siewior 68b43ab901SSebastian Andrzej Siewior case IRQ_TYPE_LEVEL_LOW: 693ffc9cebSGrant Likely reg |= BIT(4 * (d->hwirq % 8)); 70b43ab901SSebastian Andrzej Siewior break; 71b43ab901SSebastian Andrzej Siewior 72b43ab901SSebastian Andrzej Siewior default: 73b43ab901SSebastian Andrzej Siewior return -EINVAL; 74b43ab901SSebastian Andrzej Siewior } 75b43ab901SSebastian Andrzej Siewior 76b43ab901SSebastian Andrzej Siewior writel(reg, type_reg); 77b43ab901SSebastian Andrzej Siewior return 0; 78b43ab901SSebastian Andrzej Siewior } 79b43ab901SSebastian Andrzej Siewior 80b43ab901SSebastian Andrzej Siewior static irqreturn_t sdv_gpio_pub_irq_handler(int irq, void *data) 81b43ab901SSebastian Andrzej Siewior { 82b43ab901SSebastian Andrzej Siewior struct sdv_gpio_chip_data *sd = data; 83b43ab901SSebastian Andrzej Siewior u32 irq_stat = readl(sd->gpio_pub_base + GPSTR); 84b43ab901SSebastian Andrzej Siewior 85b43ab901SSebastian Andrzej Siewior irq_stat &= readl(sd->gpio_pub_base + GPIO_INT); 86b43ab901SSebastian Andrzej Siewior if (!irq_stat) 87b43ab901SSebastian Andrzej Siewior return IRQ_NONE; 88b43ab901SSebastian Andrzej Siewior 89b43ab901SSebastian Andrzej Siewior while (irq_stat) { 90b43ab901SSebastian Andrzej Siewior u32 irq_bit = __fls(irq_stat); 91b43ab901SSebastian Andrzej Siewior 92b43ab901SSebastian Andrzej Siewior irq_stat &= ~BIT(irq_bit); 933ffc9cebSGrant Likely generic_handle_irq(irq_find_mapping(sd->id, irq_bit)); 94b43ab901SSebastian Andrzej Siewior } 95b43ab901SSebastian Andrzej Siewior 96b43ab901SSebastian Andrzej Siewior return IRQ_HANDLED; 97b43ab901SSebastian Andrzej Siewior } 98b43ab901SSebastian Andrzej Siewior 99b43ab901SSebastian Andrzej Siewior static int sdv_xlate(struct irq_domain *h, struct device_node *node, 100b43ab901SSebastian Andrzej Siewior const u32 *intspec, u32 intsize, irq_hw_number_t *out_hwirq, 101b43ab901SSebastian Andrzej Siewior u32 *out_type) 102b43ab901SSebastian Andrzej Siewior { 103b43ab901SSebastian Andrzej Siewior u32 line, type; 104b43ab901SSebastian Andrzej Siewior 1055d4c9bc7SMarc Zyngier if (node != irq_domain_get_of_node(h)) 106b43ab901SSebastian Andrzej Siewior return -EINVAL; 107b43ab901SSebastian Andrzej Siewior 108b43ab901SSebastian Andrzej Siewior if (intsize < 2) 109b43ab901SSebastian Andrzej Siewior return -EINVAL; 110b43ab901SSebastian Andrzej Siewior 111b43ab901SSebastian Andrzej Siewior line = *intspec; 112b43ab901SSebastian Andrzej Siewior *out_hwirq = line; 113b43ab901SSebastian Andrzej Siewior 114b43ab901SSebastian Andrzej Siewior intspec++; 115b43ab901SSebastian Andrzej Siewior type = *intspec; 116b43ab901SSebastian Andrzej Siewior 117b43ab901SSebastian Andrzej Siewior switch (type) { 118b43ab901SSebastian Andrzej Siewior case IRQ_TYPE_LEVEL_LOW: 119b43ab901SSebastian Andrzej Siewior case IRQ_TYPE_LEVEL_HIGH: 120b43ab901SSebastian Andrzej Siewior *out_type = type; 121b43ab901SSebastian Andrzej Siewior break; 122b43ab901SSebastian Andrzej Siewior default: 123b43ab901SSebastian Andrzej Siewior return -EINVAL; 124b43ab901SSebastian Andrzej Siewior } 125b43ab901SSebastian Andrzej Siewior return 0; 126b43ab901SSebastian Andrzej Siewior } 127b43ab901SSebastian Andrzej Siewior 1280b354dc4SKrzysztof Kozlowski static const struct irq_domain_ops irq_domain_sdv_ops = { 1293ffc9cebSGrant Likely .xlate = sdv_xlate, 130b43ab901SSebastian Andrzej Siewior }; 131b43ab901SSebastian Andrzej Siewior 1323836309dSBill Pemberton static int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd, 133b43ab901SSebastian Andrzej Siewior struct pci_dev *pdev) 134b43ab901SSebastian Andrzej Siewior { 135b43ab901SSebastian Andrzej Siewior struct irq_chip_type *ct; 136b43ab901SSebastian Andrzej Siewior int ret; 137b43ab901SSebastian Andrzej Siewior 138b43ab901SSebastian Andrzej Siewior sd->irq_base = irq_alloc_descs(-1, 0, SDV_NUM_PUB_GPIOS, -1); 139b43ab901SSebastian Andrzej Siewior if (sd->irq_base < 0) 140b43ab901SSebastian Andrzej Siewior return sd->irq_base; 141b43ab901SSebastian Andrzej Siewior 142b43ab901SSebastian Andrzej Siewior /* mask + ACK all interrupt sources */ 143b43ab901SSebastian Andrzej Siewior writel(0, sd->gpio_pub_base + GPIO_INT); 144b43ab901SSebastian Andrzej Siewior writel((1 << 11) - 1, sd->gpio_pub_base + GPSTR); 145b43ab901SSebastian Andrzej Siewior 146b43ab901SSebastian Andrzej Siewior ret = request_irq(pdev->irq, sdv_gpio_pub_irq_handler, IRQF_SHARED, 147b43ab901SSebastian Andrzej Siewior "sdv_gpio", sd); 148b43ab901SSebastian Andrzej Siewior if (ret) 149b43ab901SSebastian Andrzej Siewior goto out_free_desc; 150b43ab901SSebastian Andrzej Siewior 151b43ab901SSebastian Andrzej Siewior /* 152b43ab901SSebastian Andrzej Siewior * This gpio irq controller latches level irqs. Testing shows that if 153b43ab901SSebastian Andrzej Siewior * we unmask & ACK the IRQ before the source of the interrupt is gone 154b43ab901SSebastian Andrzej Siewior * then the interrupt is active again. 155b43ab901SSebastian Andrzej Siewior */ 156b43ab901SSebastian Andrzej Siewior sd->gc = irq_alloc_generic_chip("sdv-gpio", 1, sd->irq_base, 157b43ab901SSebastian Andrzej Siewior sd->gpio_pub_base, handle_fasteoi_irq); 158b43ab901SSebastian Andrzej Siewior if (!sd->gc) { 159b43ab901SSebastian Andrzej Siewior ret = -ENOMEM; 160b43ab901SSebastian Andrzej Siewior goto out_free_irq; 161b43ab901SSebastian Andrzej Siewior } 162b43ab901SSebastian Andrzej Siewior 163b43ab901SSebastian Andrzej Siewior sd->gc->private = sd; 164b43ab901SSebastian Andrzej Siewior ct = sd->gc->chip_types; 165b43ab901SSebastian Andrzej Siewior ct->type = IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW; 166b43ab901SSebastian Andrzej Siewior ct->regs.eoi = GPSTR; 167b43ab901SSebastian Andrzej Siewior ct->regs.mask = GPIO_INT; 168b43ab901SSebastian Andrzej Siewior ct->chip.irq_mask = irq_gc_mask_clr_bit; 169b43ab901SSebastian Andrzej Siewior ct->chip.irq_unmask = irq_gc_mask_set_bit; 170b43ab901SSebastian Andrzej Siewior ct->chip.irq_eoi = irq_gc_eoi; 171b43ab901SSebastian Andrzej Siewior ct->chip.irq_set_type = sdv_gpio_pub_set_type; 172b43ab901SSebastian Andrzej Siewior 173b43ab901SSebastian Andrzej Siewior irq_setup_generic_chip(sd->gc, IRQ_MSK(SDV_NUM_PUB_GPIOS), 174b43ab901SSebastian Andrzej Siewior IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, 175b43ab901SSebastian Andrzej Siewior IRQ_LEVEL | IRQ_NOPROBE); 176b43ab901SSebastian Andrzej Siewior 1773ffc9cebSGrant Likely sd->id = irq_domain_add_legacy(pdev->dev.of_node, SDV_NUM_PUB_GPIOS, 1783ffc9cebSGrant Likely sd->irq_base, 0, &irq_domain_sdv_ops, sd); 179e6ae9195SWei Yongjun if (!sd->id) { 180e6ae9195SWei Yongjun ret = -ENODEV; 1813ffc9cebSGrant Likely goto out_free_irq; 182e6ae9195SWei Yongjun } 183b43ab901SSebastian Andrzej Siewior return 0; 184b43ab901SSebastian Andrzej Siewior out_free_irq: 185b43ab901SSebastian Andrzej Siewior free_irq(pdev->irq, sd); 186b43ab901SSebastian Andrzej Siewior out_free_desc: 187b43ab901SSebastian Andrzej Siewior irq_free_descs(sd->irq_base, SDV_NUM_PUB_GPIOS); 188b43ab901SSebastian Andrzej Siewior return ret; 189b43ab901SSebastian Andrzej Siewior } 190b43ab901SSebastian Andrzej Siewior 1913836309dSBill Pemberton static int sdv_gpio_probe(struct pci_dev *pdev, 192b43ab901SSebastian Andrzej Siewior const struct pci_device_id *pci_id) 193b43ab901SSebastian Andrzej Siewior { 194b43ab901SSebastian Andrzej Siewior struct sdv_gpio_chip_data *sd; 195b43ab901SSebastian Andrzej Siewior unsigned long addr; 196b43ab901SSebastian Andrzej Siewior const void *prop; 197b43ab901SSebastian Andrzej Siewior int len; 198b43ab901SSebastian Andrzej Siewior int ret; 199b43ab901SSebastian Andrzej Siewior u32 mux_val; 200b43ab901SSebastian Andrzej Siewior 201b43ab901SSebastian Andrzej Siewior sd = kzalloc(sizeof(struct sdv_gpio_chip_data), GFP_KERNEL); 202b43ab901SSebastian Andrzej Siewior if (!sd) 203b43ab901SSebastian Andrzej Siewior return -ENOMEM; 204b43ab901SSebastian Andrzej Siewior ret = pci_enable_device(pdev); 205b43ab901SSebastian Andrzej Siewior if (ret) { 206b43ab901SSebastian Andrzej Siewior dev_err(&pdev->dev, "can't enable device.\n"); 207b43ab901SSebastian Andrzej Siewior goto done; 208b43ab901SSebastian Andrzej Siewior } 209b43ab901SSebastian Andrzej Siewior 210b43ab901SSebastian Andrzej Siewior ret = pci_request_region(pdev, GPIO_BAR, DRV_NAME); 211b43ab901SSebastian Andrzej Siewior if (ret) { 212b43ab901SSebastian Andrzej Siewior dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", GPIO_BAR); 213b43ab901SSebastian Andrzej Siewior goto disable_pci; 214b43ab901SSebastian Andrzej Siewior } 215b43ab901SSebastian Andrzej Siewior 216b43ab901SSebastian Andrzej Siewior addr = pci_resource_start(pdev, GPIO_BAR); 217e6ae9195SWei Yongjun if (!addr) { 218e6ae9195SWei Yongjun ret = -ENODEV; 219b43ab901SSebastian Andrzej Siewior goto release_reg; 220e6ae9195SWei Yongjun } 221b43ab901SSebastian Andrzej Siewior sd->gpio_pub_base = ioremap(addr, pci_resource_len(pdev, GPIO_BAR)); 222b43ab901SSebastian Andrzej Siewior 223b43ab901SSebastian Andrzej Siewior prop = of_get_property(pdev->dev.of_node, "intel,muxctl", &len); 224b43ab901SSebastian Andrzej Siewior if (prop && len == 4) { 225b43ab901SSebastian Andrzej Siewior mux_val = of_read_number(prop, 1); 226b43ab901SSebastian Andrzej Siewior writel(mux_val, sd->gpio_pub_base + GPMUXCTL); 227b43ab901SSebastian Andrzej Siewior } 228b43ab901SSebastian Andrzej Siewior 229b43ab901SSebastian Andrzej Siewior ret = bgpio_init(&sd->bgpio, &pdev->dev, 4, 230b43ab901SSebastian Andrzej Siewior sd->gpio_pub_base + GPINR, sd->gpio_pub_base + GPOUTR, 2313e11f7b8SShawn Guo NULL, sd->gpio_pub_base + GPOER, NULL, 0); 232b43ab901SSebastian Andrzej Siewior if (ret) 233b43ab901SSebastian Andrzej Siewior goto unmap; 234b43ab901SSebastian Andrzej Siewior sd->bgpio.gc.ngpio = SDV_NUM_PUB_GPIOS; 235b43ab901SSebastian Andrzej Siewior 236b43ab901SSebastian Andrzej Siewior ret = gpiochip_add(&sd->bgpio.gc); 237b43ab901SSebastian Andrzej Siewior if (ret < 0) { 238b43ab901SSebastian Andrzej Siewior dev_err(&pdev->dev, "gpiochip_add() failed.\n"); 239b43ab901SSebastian Andrzej Siewior goto unmap; 240b43ab901SSebastian Andrzej Siewior } 241b43ab901SSebastian Andrzej Siewior 242b43ab901SSebastian Andrzej Siewior ret = sdv_register_irqsupport(sd, pdev); 243b43ab901SSebastian Andrzej Siewior if (ret) 244b43ab901SSebastian Andrzej Siewior goto unmap; 245b43ab901SSebastian Andrzej Siewior 246b43ab901SSebastian Andrzej Siewior pci_set_drvdata(pdev, sd); 247b43ab901SSebastian Andrzej Siewior dev_info(&pdev->dev, "Sodaville GPIO driver registered.\n"); 248b43ab901SSebastian Andrzej Siewior return 0; 249b43ab901SSebastian Andrzej Siewior 250b43ab901SSebastian Andrzej Siewior unmap: 251b43ab901SSebastian Andrzej Siewior iounmap(sd->gpio_pub_base); 252b43ab901SSebastian Andrzej Siewior release_reg: 253b43ab901SSebastian Andrzej Siewior pci_release_region(pdev, GPIO_BAR); 254b43ab901SSebastian Andrzej Siewior disable_pci: 255b43ab901SSebastian Andrzej Siewior pci_disable_device(pdev); 256b43ab901SSebastian Andrzej Siewior done: 257b43ab901SSebastian Andrzej Siewior kfree(sd); 258b43ab901SSebastian Andrzej Siewior return ret; 259b43ab901SSebastian Andrzej Siewior } 260b43ab901SSebastian Andrzej Siewior 261b43ab901SSebastian Andrzej Siewior static void sdv_gpio_remove(struct pci_dev *pdev) 262b43ab901SSebastian Andrzej Siewior { 263b43ab901SSebastian Andrzej Siewior struct sdv_gpio_chip_data *sd = pci_get_drvdata(pdev); 264b43ab901SSebastian Andrzej Siewior 265b43ab901SSebastian Andrzej Siewior free_irq(pdev->irq, sd); 266b43ab901SSebastian Andrzej Siewior irq_free_descs(sd->irq_base, SDV_NUM_PUB_GPIOS); 267b43ab901SSebastian Andrzej Siewior 2689f5132aeSabdoulaye berthe gpiochip_remove(&sd->bgpio.gc); 269b43ab901SSebastian Andrzej Siewior pci_release_region(pdev, GPIO_BAR); 270b43ab901SSebastian Andrzej Siewior iounmap(sd->gpio_pub_base); 271b43ab901SSebastian Andrzej Siewior pci_disable_device(pdev); 272b43ab901SSebastian Andrzej Siewior kfree(sd); 273b43ab901SSebastian Andrzej Siewior } 274b43ab901SSebastian Andrzej Siewior 27514f4a883SJingoo Han static const struct pci_device_id sdv_gpio_pci_ids[] = { 276b43ab901SSebastian Andrzej Siewior { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_SDV_GPIO) }, 277b43ab901SSebastian Andrzej Siewior { 0, }, 278b43ab901SSebastian Andrzej Siewior }; 279b43ab901SSebastian Andrzej Siewior 280b43ab901SSebastian Andrzej Siewior static struct pci_driver sdv_gpio_driver = { 281b43ab901SSebastian Andrzej Siewior .name = DRV_NAME, 282b43ab901SSebastian Andrzej Siewior .id_table = sdv_gpio_pci_ids, 283b43ab901SSebastian Andrzej Siewior .probe = sdv_gpio_probe, 284b43ab901SSebastian Andrzej Siewior .remove = sdv_gpio_remove, 285b43ab901SSebastian Andrzej Siewior }; 286b43ab901SSebastian Andrzej Siewior 28793baa65fSAxel Lin module_pci_driver(sdv_gpio_driver); 288b43ab901SSebastian Andrzej Siewior 289b43ab901SSebastian Andrzej Siewior MODULE_AUTHOR("Hans J. Koch <hjk@linutronix.de>"); 290b43ab901SSebastian Andrzej Siewior MODULE_DESCRIPTION("GPIO interface for Intel Sodaville SoCs"); 291b43ab901SSebastian Andrzej Siewior MODULE_LICENSE("GPL v2"); 292