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