18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2010 OKI SEMICONDUCTOR Co., LTD. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci#include <linux/module.h> 68c2ecf20Sopenharmony_ci#include <linux/kernel.h> 78c2ecf20Sopenharmony_ci#include <linux/slab.h> 88c2ecf20Sopenharmony_ci#include <linux/pci.h> 98c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/irq.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#define IOH_EDGE_FALLING 0 148c2ecf20Sopenharmony_ci#define IOH_EDGE_RISING BIT(0) 158c2ecf20Sopenharmony_ci#define IOH_LEVEL_L BIT(1) 168c2ecf20Sopenharmony_ci#define IOH_LEVEL_H (BIT(0) | BIT(1)) 178c2ecf20Sopenharmony_ci#define IOH_EDGE_BOTH BIT(2) 188c2ecf20Sopenharmony_ci#define IOH_IM_MASK (BIT(0) | BIT(1) | BIT(2)) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define IOH_IRQ_BASE 0 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistruct ioh_reg_comn { 238c2ecf20Sopenharmony_ci u32 ien; 248c2ecf20Sopenharmony_ci u32 istatus; 258c2ecf20Sopenharmony_ci u32 idisp; 268c2ecf20Sopenharmony_ci u32 iclr; 278c2ecf20Sopenharmony_ci u32 imask; 288c2ecf20Sopenharmony_ci u32 imaskclr; 298c2ecf20Sopenharmony_ci u32 po; 308c2ecf20Sopenharmony_ci u32 pi; 318c2ecf20Sopenharmony_ci u32 pm; 328c2ecf20Sopenharmony_ci u32 im_0; 338c2ecf20Sopenharmony_ci u32 im_1; 348c2ecf20Sopenharmony_ci u32 reserved; 358c2ecf20Sopenharmony_ci}; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistruct ioh_regs { 388c2ecf20Sopenharmony_ci struct ioh_reg_comn regs[8]; 398c2ecf20Sopenharmony_ci u32 reserve1[16]; 408c2ecf20Sopenharmony_ci u32 ioh_sel_reg[4]; 418c2ecf20Sopenharmony_ci u32 reserve2[11]; 428c2ecf20Sopenharmony_ci u32 srst; 438c2ecf20Sopenharmony_ci}; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/** 468c2ecf20Sopenharmony_ci * struct ioh_gpio_reg_data - The register store data. 478c2ecf20Sopenharmony_ci * @ien_reg: To store contents of interrupt enable register. 488c2ecf20Sopenharmony_ci * @imask_reg: To store contents of interrupt mask regist 498c2ecf20Sopenharmony_ci * @po_reg: To store contents of PO register. 508c2ecf20Sopenharmony_ci * @pm_reg: To store contents of PM register. 518c2ecf20Sopenharmony_ci * @im0_reg: To store contents of interrupt mode regist0 528c2ecf20Sopenharmony_ci * @im1_reg: To store contents of interrupt mode regist1 538c2ecf20Sopenharmony_ci * @use_sel_reg: To store contents of GPIO_USE_SEL0~3 548c2ecf20Sopenharmony_ci */ 558c2ecf20Sopenharmony_cistruct ioh_gpio_reg_data { 568c2ecf20Sopenharmony_ci u32 ien_reg; 578c2ecf20Sopenharmony_ci u32 imask_reg; 588c2ecf20Sopenharmony_ci u32 po_reg; 598c2ecf20Sopenharmony_ci u32 pm_reg; 608c2ecf20Sopenharmony_ci u32 im0_reg; 618c2ecf20Sopenharmony_ci u32 im1_reg; 628c2ecf20Sopenharmony_ci u32 use_sel_reg; 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/** 668c2ecf20Sopenharmony_ci * struct ioh_gpio - GPIO private data structure. 678c2ecf20Sopenharmony_ci * @base: PCI base address of Memory mapped I/O register. 688c2ecf20Sopenharmony_ci * @reg: Memory mapped IOH GPIO register list. 698c2ecf20Sopenharmony_ci * @dev: Pointer to device structure. 708c2ecf20Sopenharmony_ci * @gpio: Data for GPIO infrastructure. 718c2ecf20Sopenharmony_ci * @ioh_gpio_reg: Memory mapped Register data is saved here 728c2ecf20Sopenharmony_ci * when suspend. 738c2ecf20Sopenharmony_ci * @gpio_use_sel: Save GPIO_USE_SEL1~4 register for PM 748c2ecf20Sopenharmony_ci * @ch: Indicate GPIO channel 758c2ecf20Sopenharmony_ci * @irq_base: Save base of IRQ number for interrupt 768c2ecf20Sopenharmony_ci * @spinlock: Used for register access protection 778c2ecf20Sopenharmony_ci */ 788c2ecf20Sopenharmony_cistruct ioh_gpio { 798c2ecf20Sopenharmony_ci void __iomem *base; 808c2ecf20Sopenharmony_ci struct ioh_regs __iomem *reg; 818c2ecf20Sopenharmony_ci struct device *dev; 828c2ecf20Sopenharmony_ci struct gpio_chip gpio; 838c2ecf20Sopenharmony_ci struct ioh_gpio_reg_data ioh_gpio_reg; 848c2ecf20Sopenharmony_ci u32 gpio_use_sel; 858c2ecf20Sopenharmony_ci int ch; 868c2ecf20Sopenharmony_ci int irq_base; 878c2ecf20Sopenharmony_ci spinlock_t spinlock; 888c2ecf20Sopenharmony_ci}; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12}; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci u32 reg_val; 958c2ecf20Sopenharmony_ci struct ioh_gpio *chip = gpiochip_get_data(gpio); 968c2ecf20Sopenharmony_ci unsigned long flags; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->spinlock, flags); 998c2ecf20Sopenharmony_ci reg_val = ioread32(&chip->reg->regs[chip->ch].po); 1008c2ecf20Sopenharmony_ci if (val) 1018c2ecf20Sopenharmony_ci reg_val |= (1 << nr); 1028c2ecf20Sopenharmony_ci else 1038c2ecf20Sopenharmony_ci reg_val &= ~(1 << nr); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci iowrite32(reg_val, &chip->reg->regs[chip->ch].po); 1068c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->spinlock, flags); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_cistatic int ioh_gpio_get(struct gpio_chip *gpio, unsigned nr) 1108c2ecf20Sopenharmony_ci{ 1118c2ecf20Sopenharmony_ci struct ioh_gpio *chip = gpiochip_get_data(gpio); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return !!(ioread32(&chip->reg->regs[chip->ch].pi) & (1 << nr)); 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic int ioh_gpio_direction_output(struct gpio_chip *gpio, unsigned nr, 1178c2ecf20Sopenharmony_ci int val) 1188c2ecf20Sopenharmony_ci{ 1198c2ecf20Sopenharmony_ci struct ioh_gpio *chip = gpiochip_get_data(gpio); 1208c2ecf20Sopenharmony_ci u32 pm; 1218c2ecf20Sopenharmony_ci u32 reg_val; 1228c2ecf20Sopenharmony_ci unsigned long flags; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->spinlock, flags); 1258c2ecf20Sopenharmony_ci pm = ioread32(&chip->reg->regs[chip->ch].pm) & 1268c2ecf20Sopenharmony_ci ((1 << num_ports[chip->ch]) - 1); 1278c2ecf20Sopenharmony_ci pm |= (1 << nr); 1288c2ecf20Sopenharmony_ci iowrite32(pm, &chip->reg->regs[chip->ch].pm); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci reg_val = ioread32(&chip->reg->regs[chip->ch].po); 1318c2ecf20Sopenharmony_ci if (val) 1328c2ecf20Sopenharmony_ci reg_val |= (1 << nr); 1338c2ecf20Sopenharmony_ci else 1348c2ecf20Sopenharmony_ci reg_val &= ~(1 << nr); 1358c2ecf20Sopenharmony_ci iowrite32(reg_val, &chip->reg->regs[chip->ch].po); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->spinlock, flags); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic int ioh_gpio_direction_input(struct gpio_chip *gpio, unsigned nr) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct ioh_gpio *chip = gpiochip_get_data(gpio); 1458c2ecf20Sopenharmony_ci u32 pm; 1468c2ecf20Sopenharmony_ci unsigned long flags; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->spinlock, flags); 1498c2ecf20Sopenharmony_ci pm = ioread32(&chip->reg->regs[chip->ch].pm) & 1508c2ecf20Sopenharmony_ci ((1 << num_ports[chip->ch]) - 1); 1518c2ecf20Sopenharmony_ci pm &= ~(1 << nr); 1528c2ecf20Sopenharmony_ci iowrite32(pm, &chip->reg->regs[chip->ch].pm); 1538c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->spinlock, flags); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci return 0; 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 1598c2ecf20Sopenharmony_ci/* 1608c2ecf20Sopenharmony_ci * Save register configuration and disable interrupts. 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_cistatic void ioh_gpio_save_reg_conf(struct ioh_gpio *chip) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci int i; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci for (i = 0; i < 8; i ++, chip++) { 1678c2ecf20Sopenharmony_ci chip->ioh_gpio_reg.po_reg = 1688c2ecf20Sopenharmony_ci ioread32(&chip->reg->regs[chip->ch].po); 1698c2ecf20Sopenharmony_ci chip->ioh_gpio_reg.pm_reg = 1708c2ecf20Sopenharmony_ci ioread32(&chip->reg->regs[chip->ch].pm); 1718c2ecf20Sopenharmony_ci chip->ioh_gpio_reg.ien_reg = 1728c2ecf20Sopenharmony_ci ioread32(&chip->reg->regs[chip->ch].ien); 1738c2ecf20Sopenharmony_ci chip->ioh_gpio_reg.imask_reg = 1748c2ecf20Sopenharmony_ci ioread32(&chip->reg->regs[chip->ch].imask); 1758c2ecf20Sopenharmony_ci chip->ioh_gpio_reg.im0_reg = 1768c2ecf20Sopenharmony_ci ioread32(&chip->reg->regs[chip->ch].im_0); 1778c2ecf20Sopenharmony_ci chip->ioh_gpio_reg.im1_reg = 1788c2ecf20Sopenharmony_ci ioread32(&chip->reg->regs[chip->ch].im_1); 1798c2ecf20Sopenharmony_ci if (i < 4) 1808c2ecf20Sopenharmony_ci chip->ioh_gpio_reg.use_sel_reg = 1818c2ecf20Sopenharmony_ci ioread32(&chip->reg->ioh_sel_reg[i]); 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci/* 1868c2ecf20Sopenharmony_ci * This function restores the register configuration of the GPIO device. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_cistatic void ioh_gpio_restore_reg_conf(struct ioh_gpio *chip) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci int i; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci for (i = 0; i < 8; i ++, chip++) { 1938c2ecf20Sopenharmony_ci iowrite32(chip->ioh_gpio_reg.po_reg, 1948c2ecf20Sopenharmony_ci &chip->reg->regs[chip->ch].po); 1958c2ecf20Sopenharmony_ci iowrite32(chip->ioh_gpio_reg.pm_reg, 1968c2ecf20Sopenharmony_ci &chip->reg->regs[chip->ch].pm); 1978c2ecf20Sopenharmony_ci iowrite32(chip->ioh_gpio_reg.ien_reg, 1988c2ecf20Sopenharmony_ci &chip->reg->regs[chip->ch].ien); 1998c2ecf20Sopenharmony_ci iowrite32(chip->ioh_gpio_reg.imask_reg, 2008c2ecf20Sopenharmony_ci &chip->reg->regs[chip->ch].imask); 2018c2ecf20Sopenharmony_ci iowrite32(chip->ioh_gpio_reg.im0_reg, 2028c2ecf20Sopenharmony_ci &chip->reg->regs[chip->ch].im_0); 2038c2ecf20Sopenharmony_ci iowrite32(chip->ioh_gpio_reg.im1_reg, 2048c2ecf20Sopenharmony_ci &chip->reg->regs[chip->ch].im_1); 2058c2ecf20Sopenharmony_ci if (i < 4) 2068c2ecf20Sopenharmony_ci iowrite32(chip->ioh_gpio_reg.use_sel_reg, 2078c2ecf20Sopenharmony_ci &chip->reg->ioh_sel_reg[i]); 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci#endif 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_cistatic int ioh_gpio_to_irq(struct gpio_chip *gpio, unsigned offset) 2138c2ecf20Sopenharmony_ci{ 2148c2ecf20Sopenharmony_ci struct ioh_gpio *chip = gpiochip_get_data(gpio); 2158c2ecf20Sopenharmony_ci return chip->irq_base + offset; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic void ioh_gpio_setup(struct ioh_gpio *chip, int num_port) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct gpio_chip *gpio = &chip->gpio; 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci gpio->label = dev_name(chip->dev); 2238c2ecf20Sopenharmony_ci gpio->owner = THIS_MODULE; 2248c2ecf20Sopenharmony_ci gpio->direction_input = ioh_gpio_direction_input; 2258c2ecf20Sopenharmony_ci gpio->get = ioh_gpio_get; 2268c2ecf20Sopenharmony_ci gpio->direction_output = ioh_gpio_direction_output; 2278c2ecf20Sopenharmony_ci gpio->set = ioh_gpio_set; 2288c2ecf20Sopenharmony_ci gpio->dbg_show = NULL; 2298c2ecf20Sopenharmony_ci gpio->base = -1; 2308c2ecf20Sopenharmony_ci gpio->ngpio = num_port; 2318c2ecf20Sopenharmony_ci gpio->can_sleep = false; 2328c2ecf20Sopenharmony_ci gpio->to_irq = ioh_gpio_to_irq; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int ioh_irq_type(struct irq_data *d, unsigned int type) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci u32 im; 2388c2ecf20Sopenharmony_ci void __iomem *im_reg; 2398c2ecf20Sopenharmony_ci u32 ien; 2408c2ecf20Sopenharmony_ci u32 im_pos; 2418c2ecf20Sopenharmony_ci int ch; 2428c2ecf20Sopenharmony_ci unsigned long flags; 2438c2ecf20Sopenharmony_ci u32 val; 2448c2ecf20Sopenharmony_ci int irq = d->irq; 2458c2ecf20Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 2468c2ecf20Sopenharmony_ci struct ioh_gpio *chip = gc->private; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci ch = irq - chip->irq_base; 2498c2ecf20Sopenharmony_ci if (irq <= chip->irq_base + 7) { 2508c2ecf20Sopenharmony_ci im_reg = &chip->reg->regs[chip->ch].im_0; 2518c2ecf20Sopenharmony_ci im_pos = ch; 2528c2ecf20Sopenharmony_ci } else { 2538c2ecf20Sopenharmony_ci im_reg = &chip->reg->regs[chip->ch].im_1; 2548c2ecf20Sopenharmony_ci im_pos = ch - 8; 2558c2ecf20Sopenharmony_ci } 2568c2ecf20Sopenharmony_ci dev_dbg(chip->dev, "%s:irq=%d type=%d ch=%d pos=%d type=%d\n", 2578c2ecf20Sopenharmony_ci __func__, irq, type, ch, im_pos, type); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->spinlock, flags); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci switch (type) { 2628c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 2638c2ecf20Sopenharmony_ci val = IOH_EDGE_RISING; 2648c2ecf20Sopenharmony_ci break; 2658c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 2668c2ecf20Sopenharmony_ci val = IOH_EDGE_FALLING; 2678c2ecf20Sopenharmony_ci break; 2688c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_BOTH: 2698c2ecf20Sopenharmony_ci val = IOH_EDGE_BOTH; 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 2728c2ecf20Sopenharmony_ci val = IOH_LEVEL_H; 2738c2ecf20Sopenharmony_ci break; 2748c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 2758c2ecf20Sopenharmony_ci val = IOH_LEVEL_L; 2768c2ecf20Sopenharmony_ci break; 2778c2ecf20Sopenharmony_ci case IRQ_TYPE_PROBE: 2788c2ecf20Sopenharmony_ci goto end; 2798c2ecf20Sopenharmony_ci default: 2808c2ecf20Sopenharmony_ci dev_warn(chip->dev, "%s: unknown type(%dd)", 2818c2ecf20Sopenharmony_ci __func__, type); 2828c2ecf20Sopenharmony_ci goto end; 2838c2ecf20Sopenharmony_ci } 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* Set interrupt mode */ 2868c2ecf20Sopenharmony_ci im = ioread32(im_reg) & ~(IOH_IM_MASK << (im_pos * 4)); 2878c2ecf20Sopenharmony_ci iowrite32(im | (val << (im_pos * 4)), im_reg); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* iclr */ 2908c2ecf20Sopenharmony_ci iowrite32(BIT(ch), &chip->reg->regs[chip->ch].iclr); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* IMASKCLR */ 2938c2ecf20Sopenharmony_ci iowrite32(BIT(ch), &chip->reg->regs[chip->ch].imaskclr); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci /* Enable interrupt */ 2968c2ecf20Sopenharmony_ci ien = ioread32(&chip->reg->regs[chip->ch].ien); 2978c2ecf20Sopenharmony_ci iowrite32(ien | BIT(ch), &chip->reg->regs[chip->ch].ien); 2988c2ecf20Sopenharmony_ciend: 2998c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->spinlock, flags); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic void ioh_irq_unmask(struct irq_data *d) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 3078c2ecf20Sopenharmony_ci struct ioh_gpio *chip = gc->private; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci iowrite32(1 << (d->irq - chip->irq_base), 3108c2ecf20Sopenharmony_ci &chip->reg->regs[chip->ch].imaskclr); 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic void ioh_irq_mask(struct irq_data *d) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 3168c2ecf20Sopenharmony_ci struct ioh_gpio *chip = gc->private; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci iowrite32(1 << (d->irq - chip->irq_base), 3198c2ecf20Sopenharmony_ci &chip->reg->regs[chip->ch].imask); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic void ioh_irq_disable(struct irq_data *d) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 3258c2ecf20Sopenharmony_ci struct ioh_gpio *chip = gc->private; 3268c2ecf20Sopenharmony_ci unsigned long flags; 3278c2ecf20Sopenharmony_ci u32 ien; 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->spinlock, flags); 3308c2ecf20Sopenharmony_ci ien = ioread32(&chip->reg->regs[chip->ch].ien); 3318c2ecf20Sopenharmony_ci ien &= ~(1 << (d->irq - chip->irq_base)); 3328c2ecf20Sopenharmony_ci iowrite32(ien, &chip->reg->regs[chip->ch].ien); 3338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->spinlock, flags); 3348c2ecf20Sopenharmony_ci} 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_cistatic void ioh_irq_enable(struct irq_data *d) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d); 3398c2ecf20Sopenharmony_ci struct ioh_gpio *chip = gc->private; 3408c2ecf20Sopenharmony_ci unsigned long flags; 3418c2ecf20Sopenharmony_ci u32 ien; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->spinlock, flags); 3448c2ecf20Sopenharmony_ci ien = ioread32(&chip->reg->regs[chip->ch].ien); 3458c2ecf20Sopenharmony_ci ien |= 1 << (d->irq - chip->irq_base); 3468c2ecf20Sopenharmony_ci iowrite32(ien, &chip->reg->regs[chip->ch].ien); 3478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->spinlock, flags); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic irqreturn_t ioh_gpio_handler(int irq, void *dev_id) 3518c2ecf20Sopenharmony_ci{ 3528c2ecf20Sopenharmony_ci struct ioh_gpio *chip = dev_id; 3538c2ecf20Sopenharmony_ci u32 reg_val; 3548c2ecf20Sopenharmony_ci int i, j; 3558c2ecf20Sopenharmony_ci int ret = IRQ_NONE; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++, chip++) { 3588c2ecf20Sopenharmony_ci reg_val = ioread32(&chip->reg->regs[i].istatus); 3598c2ecf20Sopenharmony_ci for (j = 0; j < num_ports[i]; j++) { 3608c2ecf20Sopenharmony_ci if (reg_val & BIT(j)) { 3618c2ecf20Sopenharmony_ci dev_dbg(chip->dev, 3628c2ecf20Sopenharmony_ci "%s:[%d]:irq=%d status=0x%x\n", 3638c2ecf20Sopenharmony_ci __func__, j, irq, reg_val); 3648c2ecf20Sopenharmony_ci iowrite32(BIT(j), 3658c2ecf20Sopenharmony_ci &chip->reg->regs[chip->ch].iclr); 3668c2ecf20Sopenharmony_ci generic_handle_irq(chip->irq_base + j); 3678c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci return ret; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic int ioh_gpio_alloc_generic_chip(struct ioh_gpio *chip, 3758c2ecf20Sopenharmony_ci unsigned int irq_start, 3768c2ecf20Sopenharmony_ci unsigned int num) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci struct irq_chip_generic *gc; 3798c2ecf20Sopenharmony_ci struct irq_chip_type *ct; 3808c2ecf20Sopenharmony_ci int rv; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci gc = devm_irq_alloc_generic_chip(chip->dev, "ioh_gpio", 1, irq_start, 3838c2ecf20Sopenharmony_ci chip->base, handle_simple_irq); 3848c2ecf20Sopenharmony_ci if (!gc) 3858c2ecf20Sopenharmony_ci return -ENOMEM; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci gc->private = chip; 3888c2ecf20Sopenharmony_ci ct = gc->chip_types; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci ct->chip.irq_mask = ioh_irq_mask; 3918c2ecf20Sopenharmony_ci ct->chip.irq_unmask = ioh_irq_unmask; 3928c2ecf20Sopenharmony_ci ct->chip.irq_set_type = ioh_irq_type; 3938c2ecf20Sopenharmony_ci ct->chip.irq_disable = ioh_irq_disable; 3948c2ecf20Sopenharmony_ci ct->chip.irq_enable = ioh_irq_enable; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci rv = devm_irq_setup_generic_chip(chip->dev, gc, IRQ_MSK(num), 3978c2ecf20Sopenharmony_ci IRQ_GC_INIT_MASK_CACHE, 3988c2ecf20Sopenharmony_ci IRQ_NOREQUEST | IRQ_NOPROBE, 0); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci return rv; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cistatic int ioh_gpio_probe(struct pci_dev *pdev, 4048c2ecf20Sopenharmony_ci const struct pci_device_id *id) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci int ret; 4078c2ecf20Sopenharmony_ci int i, j; 4088c2ecf20Sopenharmony_ci struct ioh_gpio *chip; 4098c2ecf20Sopenharmony_ci void __iomem *base; 4108c2ecf20Sopenharmony_ci void *chip_save; 4118c2ecf20Sopenharmony_ci int irq_base; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci ret = pci_enable_device(pdev); 4148c2ecf20Sopenharmony_ci if (ret) { 4158c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s : pci_enable_device failed", __func__); 4168c2ecf20Sopenharmony_ci goto err_pci_enable; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ret = pci_request_regions(pdev, KBUILD_MODNAME); 4208c2ecf20Sopenharmony_ci if (ret) { 4218c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "pci_request_regions failed-%d", ret); 4228c2ecf20Sopenharmony_ci goto err_request_regions; 4238c2ecf20Sopenharmony_ci } 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci base = pci_iomap(pdev, 1, 0); 4268c2ecf20Sopenharmony_ci if (!base) { 4278c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s : pci_iomap failed", __func__); 4288c2ecf20Sopenharmony_ci ret = -ENOMEM; 4298c2ecf20Sopenharmony_ci goto err_iomap; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci chip_save = kcalloc(8, sizeof(*chip), GFP_KERNEL); 4338c2ecf20Sopenharmony_ci if (chip_save == NULL) { 4348c2ecf20Sopenharmony_ci ret = -ENOMEM; 4358c2ecf20Sopenharmony_ci goto err_kzalloc; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci chip = chip_save; 4398c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++, chip++) { 4408c2ecf20Sopenharmony_ci chip->dev = &pdev->dev; 4418c2ecf20Sopenharmony_ci chip->base = base; 4428c2ecf20Sopenharmony_ci chip->reg = chip->base; 4438c2ecf20Sopenharmony_ci chip->ch = i; 4448c2ecf20Sopenharmony_ci spin_lock_init(&chip->spinlock); 4458c2ecf20Sopenharmony_ci ioh_gpio_setup(chip, num_ports[i]); 4468c2ecf20Sopenharmony_ci ret = gpiochip_add_data(&chip->gpio, chip); 4478c2ecf20Sopenharmony_ci if (ret) { 4488c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "IOH gpio: Failed to register GPIO\n"); 4498c2ecf20Sopenharmony_ci goto err_gpiochip_add; 4508c2ecf20Sopenharmony_ci } 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci chip = chip_save; 4548c2ecf20Sopenharmony_ci for (j = 0; j < 8; j++, chip++) { 4558c2ecf20Sopenharmony_ci irq_base = devm_irq_alloc_descs(&pdev->dev, -1, IOH_IRQ_BASE, 4568c2ecf20Sopenharmony_ci num_ports[j], NUMA_NO_NODE); 4578c2ecf20Sopenharmony_ci if (irq_base < 0) { 4588c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 4598c2ecf20Sopenharmony_ci "ml_ioh_gpio: Failed to get IRQ base num\n"); 4608c2ecf20Sopenharmony_ci ret = irq_base; 4618c2ecf20Sopenharmony_ci goto err_gpiochip_add; 4628c2ecf20Sopenharmony_ci } 4638c2ecf20Sopenharmony_ci chip->irq_base = irq_base; 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci ret = ioh_gpio_alloc_generic_chip(chip, 4668c2ecf20Sopenharmony_ci irq_base, num_ports[j]); 4678c2ecf20Sopenharmony_ci if (ret) 4688c2ecf20Sopenharmony_ci goto err_gpiochip_add; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci chip = chip_save; 4728c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, pdev->irq, ioh_gpio_handler, 4738c2ecf20Sopenharmony_ci IRQF_SHARED, KBUILD_MODNAME, chip); 4748c2ecf20Sopenharmony_ci if (ret != 0) { 4758c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 4768c2ecf20Sopenharmony_ci "%s request_irq failed\n", __func__); 4778c2ecf20Sopenharmony_ci goto err_gpiochip_add; 4788c2ecf20Sopenharmony_ci } 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, chip); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci return 0; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_cierr_gpiochip_add: 4858c2ecf20Sopenharmony_ci chip = chip_save; 4868c2ecf20Sopenharmony_ci while (--i >= 0) { 4878c2ecf20Sopenharmony_ci gpiochip_remove(&chip->gpio); 4888c2ecf20Sopenharmony_ci chip++; 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci kfree(chip_save); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_cierr_kzalloc: 4938c2ecf20Sopenharmony_ci pci_iounmap(pdev, base); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cierr_iomap: 4968c2ecf20Sopenharmony_ci pci_release_regions(pdev); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_cierr_request_regions: 4998c2ecf20Sopenharmony_ci pci_disable_device(pdev); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_cierr_pci_enable: 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s Failed returns %d\n", __func__, ret); 5048c2ecf20Sopenharmony_ci return ret; 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic void ioh_gpio_remove(struct pci_dev *pdev) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci int i; 5108c2ecf20Sopenharmony_ci struct ioh_gpio *chip = pci_get_drvdata(pdev); 5118c2ecf20Sopenharmony_ci void *chip_save; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci chip_save = chip; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++, chip++) 5168c2ecf20Sopenharmony_ci gpiochip_remove(&chip->gpio); 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci chip = chip_save; 5198c2ecf20Sopenharmony_ci pci_iounmap(pdev, chip->base); 5208c2ecf20Sopenharmony_ci pci_release_regions(pdev); 5218c2ecf20Sopenharmony_ci pci_disable_device(pdev); 5228c2ecf20Sopenharmony_ci kfree(chip); 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 5268c2ecf20Sopenharmony_cistatic int ioh_gpio_suspend(struct pci_dev *pdev, pm_message_t state) 5278c2ecf20Sopenharmony_ci{ 5288c2ecf20Sopenharmony_ci s32 ret; 5298c2ecf20Sopenharmony_ci struct ioh_gpio *chip = pci_get_drvdata(pdev); 5308c2ecf20Sopenharmony_ci unsigned long flags; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->spinlock, flags); 5338c2ecf20Sopenharmony_ci ioh_gpio_save_reg_conf(chip); 5348c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->spinlock, flags); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci ret = pci_save_state(pdev); 5378c2ecf20Sopenharmony_ci if (ret) { 5388c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "pci_save_state Failed-%d\n", ret); 5398c2ecf20Sopenharmony_ci return ret; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci pci_disable_device(pdev); 5428c2ecf20Sopenharmony_ci pci_set_power_state(pdev, PCI_D0); 5438c2ecf20Sopenharmony_ci ret = pci_enable_wake(pdev, PCI_D0, 1); 5448c2ecf20Sopenharmony_ci if (ret) 5458c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "pci_enable_wake Failed -%d\n", ret); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return 0; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic int ioh_gpio_resume(struct pci_dev *pdev) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci s32 ret; 5538c2ecf20Sopenharmony_ci struct ioh_gpio *chip = pci_get_drvdata(pdev); 5548c2ecf20Sopenharmony_ci unsigned long flags; 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci ret = pci_enable_wake(pdev, PCI_D0, 0); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci pci_set_power_state(pdev, PCI_D0); 5598c2ecf20Sopenharmony_ci ret = pci_enable_device(pdev); 5608c2ecf20Sopenharmony_ci if (ret) { 5618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "pci_enable_device Failed-%d ", ret); 5628c2ecf20Sopenharmony_ci return ret; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci pci_restore_state(pdev); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->spinlock, flags); 5678c2ecf20Sopenharmony_ci iowrite32(0x01, &chip->reg->srst); 5688c2ecf20Sopenharmony_ci iowrite32(0x00, &chip->reg->srst); 5698c2ecf20Sopenharmony_ci ioh_gpio_restore_reg_conf(chip); 5708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->spinlock, flags); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci return 0; 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci#else 5758c2ecf20Sopenharmony_ci#define ioh_gpio_suspend NULL 5768c2ecf20Sopenharmony_ci#define ioh_gpio_resume NULL 5778c2ecf20Sopenharmony_ci#endif 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic const struct pci_device_id ioh_gpio_pcidev_id[] = { 5808c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x802E) }, 5818c2ecf20Sopenharmony_ci { 0, } 5828c2ecf20Sopenharmony_ci}; 5838c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, ioh_gpio_pcidev_id); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic struct pci_driver ioh_gpio_driver = { 5868c2ecf20Sopenharmony_ci .name = "ml_ioh_gpio", 5878c2ecf20Sopenharmony_ci .id_table = ioh_gpio_pcidev_id, 5888c2ecf20Sopenharmony_ci .probe = ioh_gpio_probe, 5898c2ecf20Sopenharmony_ci .remove = ioh_gpio_remove, 5908c2ecf20Sopenharmony_ci .suspend = ioh_gpio_suspend, 5918c2ecf20Sopenharmony_ci .resume = ioh_gpio_resume 5928c2ecf20Sopenharmony_ci}; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cimodule_pci_driver(ioh_gpio_driver); 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OKI SEMICONDUCTOR ML-IOH series GPIO Driver"); 5978c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 598