162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Support for the GPIO/IRQ expander chips present on several HTC phones.
362306a36Sopenharmony_ci * These are implemented in CPLD chips present on the board.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2007 Kevin O'Connor <kevin@koconnor.net>
662306a36Sopenharmony_ci * Copyright (c) 2007 Philipp Zabel <philipp.zabel@gmail.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * This file may be distributed under the terms of the GNU GPL license.
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/errno.h>
1362306a36Sopenharmony_ci#include <linux/interrupt.h>
1462306a36Sopenharmony_ci#include <linux/irq.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/spinlock.h>
1762306a36Sopenharmony_ci#include <linux/platform_data/gpio-htc-egpio.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/init.h>
2162306a36Sopenharmony_ci#include <linux/gpio/driver.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct egpio_chip {
2462306a36Sopenharmony_ci	int              reg_start;
2562306a36Sopenharmony_ci	int              cached_values;
2662306a36Sopenharmony_ci	unsigned long    is_out;
2762306a36Sopenharmony_ci	struct device    *dev;
2862306a36Sopenharmony_ci	struct gpio_chip chip;
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistruct egpio_info {
3262306a36Sopenharmony_ci	spinlock_t        lock;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/* iomem info */
3562306a36Sopenharmony_ci	void __iomem      *base_addr;
3662306a36Sopenharmony_ci	int               bus_shift;	/* byte shift */
3762306a36Sopenharmony_ci	int               reg_shift;	/* bit shift */
3862306a36Sopenharmony_ci	int               reg_mask;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	/* irq info */
4162306a36Sopenharmony_ci	int               ack_register;
4262306a36Sopenharmony_ci	int               ack_write;
4362306a36Sopenharmony_ci	u16               irqs_enabled;
4462306a36Sopenharmony_ci	uint              irq_start;
4562306a36Sopenharmony_ci	int               nirqs;
4662306a36Sopenharmony_ci	uint              chained_irq;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/* egpio info */
4962306a36Sopenharmony_ci	struct egpio_chip *chip;
5062306a36Sopenharmony_ci	int               nchips;
5162306a36Sopenharmony_ci};
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic inline void egpio_writew(u16 value, struct egpio_info *ei, int reg)
5462306a36Sopenharmony_ci{
5562306a36Sopenharmony_ci	writew(value, ei->base_addr + (reg << ei->bus_shift));
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic inline u16 egpio_readw(struct egpio_info *ei, int reg)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	return readw(ei->base_addr + (reg << ei->bus_shift));
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci/*
6462306a36Sopenharmony_ci * IRQs
6562306a36Sopenharmony_ci */
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_cistatic inline void ack_irqs(struct egpio_info *ei)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	egpio_writew(ei->ack_write, ei, ei->ack_register);
7062306a36Sopenharmony_ci	pr_debug("EGPIO ack - write %x to base+%x\n",
7162306a36Sopenharmony_ci			ei->ack_write, ei->ack_register << ei->bus_shift);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic void egpio_ack(struct irq_data *data)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci/* There does not appear to be a way to proactively mask interrupts
7962306a36Sopenharmony_ci * on the egpio chip itself.  So, we simply ignore interrupts that
8062306a36Sopenharmony_ci * aren't desired. */
8162306a36Sopenharmony_cistatic void egpio_mask(struct irq_data *data)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	struct egpio_info *ei = irq_data_get_irq_chip_data(data);
8462306a36Sopenharmony_ci	ei->irqs_enabled &= ~(1 << (data->irq - ei->irq_start));
8562306a36Sopenharmony_ci	pr_debug("EGPIO mask %d %04x\n", data->irq, ei->irqs_enabled);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic void egpio_unmask(struct irq_data *data)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct egpio_info *ei = irq_data_get_irq_chip_data(data);
9162306a36Sopenharmony_ci	ei->irqs_enabled |= 1 << (data->irq - ei->irq_start);
9262306a36Sopenharmony_ci	pr_debug("EGPIO unmask %d %04x\n", data->irq, ei->irqs_enabled);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic struct irq_chip egpio_muxed_chip = {
9662306a36Sopenharmony_ci	.name		= "htc-egpio",
9762306a36Sopenharmony_ci	.irq_ack	= egpio_ack,
9862306a36Sopenharmony_ci	.irq_mask	= egpio_mask,
9962306a36Sopenharmony_ci	.irq_unmask	= egpio_unmask,
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic void egpio_handler(struct irq_desc *desc)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct egpio_info *ei = irq_desc_get_handler_data(desc);
10562306a36Sopenharmony_ci	int irqpin;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	/* Read current pins. */
10862306a36Sopenharmony_ci	unsigned long readval = egpio_readw(ei, ei->ack_register);
10962306a36Sopenharmony_ci	pr_debug("IRQ reg: %x\n", (unsigned int)readval);
11062306a36Sopenharmony_ci	/* Ack/unmask interrupts. */
11162306a36Sopenharmony_ci	ack_irqs(ei);
11262306a36Sopenharmony_ci	/* Process all set pins. */
11362306a36Sopenharmony_ci	readval &= ei->irqs_enabled;
11462306a36Sopenharmony_ci	for_each_set_bit(irqpin, &readval, ei->nirqs) {
11562306a36Sopenharmony_ci		/* Run irq handler */
11662306a36Sopenharmony_ci		pr_debug("got IRQ %d\n", irqpin);
11762306a36Sopenharmony_ci		generic_handle_irq(ei->irq_start + irqpin);
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic inline int egpio_pos(struct egpio_info *ei, int bit)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	return bit >> ei->reg_shift;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic inline int egpio_bit(struct egpio_info *ei, int bit)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	return 1 << (bit & ((1 << ei->reg_shift)-1));
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/*
13262306a36Sopenharmony_ci * Input pins
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic int egpio_get(struct gpio_chip *chip, unsigned offset)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	struct egpio_chip *egpio;
13862306a36Sopenharmony_ci	struct egpio_info *ei;
13962306a36Sopenharmony_ci	unsigned           bit;
14062306a36Sopenharmony_ci	int                reg;
14162306a36Sopenharmony_ci	int                value;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	pr_debug("egpio_get_value(%d)\n", chip->base + offset);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci	egpio = gpiochip_get_data(chip);
14662306a36Sopenharmony_ci	ei    = dev_get_drvdata(egpio->dev);
14762306a36Sopenharmony_ci	bit   = egpio_bit(ei, offset);
14862306a36Sopenharmony_ci	reg   = egpio->reg_start + egpio_pos(ei, offset);
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (test_bit(offset, &egpio->is_out)) {
15162306a36Sopenharmony_ci		return !!(egpio->cached_values & (1 << offset));
15262306a36Sopenharmony_ci	} else {
15362306a36Sopenharmony_ci		value = egpio_readw(ei, reg);
15462306a36Sopenharmony_ci		pr_debug("readw(%p + %x) = %x\n",
15562306a36Sopenharmony_ci			 ei->base_addr, reg << ei->bus_shift, value);
15662306a36Sopenharmony_ci		return !!(value & bit);
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci}
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_cistatic int egpio_direction_input(struct gpio_chip *chip, unsigned offset)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	struct egpio_chip *egpio;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	egpio = gpiochip_get_data(chip);
16562306a36Sopenharmony_ci	return test_bit(offset, &egpio->is_out) ? -EINVAL : 0;
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci/*
17062306a36Sopenharmony_ci * Output pins
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cistatic void egpio_set(struct gpio_chip *chip, unsigned offset, int value)
17462306a36Sopenharmony_ci{
17562306a36Sopenharmony_ci	unsigned long     flag;
17662306a36Sopenharmony_ci	struct egpio_chip *egpio;
17762306a36Sopenharmony_ci	struct egpio_info *ei;
17862306a36Sopenharmony_ci	int               pos;
17962306a36Sopenharmony_ci	int               reg;
18062306a36Sopenharmony_ci	int               shift;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	pr_debug("egpio_set(%s, %d(%d), %d)\n",
18362306a36Sopenharmony_ci			chip->label, offset, offset+chip->base, value);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	egpio = gpiochip_get_data(chip);
18662306a36Sopenharmony_ci	ei    = dev_get_drvdata(egpio->dev);
18762306a36Sopenharmony_ci	pos   = egpio_pos(ei, offset);
18862306a36Sopenharmony_ci	reg   = egpio->reg_start + pos;
18962306a36Sopenharmony_ci	shift = pos << ei->reg_shift;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	pr_debug("egpio %s: reg %d = 0x%04x\n", value ? "set" : "clear",
19262306a36Sopenharmony_ci			reg, (egpio->cached_values >> shift) & ei->reg_mask);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	spin_lock_irqsave(&ei->lock, flag);
19562306a36Sopenharmony_ci	if (value)
19662306a36Sopenharmony_ci		egpio->cached_values |= (1 << offset);
19762306a36Sopenharmony_ci	else
19862306a36Sopenharmony_ci		egpio->cached_values &= ~(1 << offset);
19962306a36Sopenharmony_ci	egpio_writew((egpio->cached_values >> shift) & ei->reg_mask, ei, reg);
20062306a36Sopenharmony_ci	spin_unlock_irqrestore(&ei->lock, flag);
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic int egpio_direction_output(struct gpio_chip *chip,
20462306a36Sopenharmony_ci					unsigned offset, int value)
20562306a36Sopenharmony_ci{
20662306a36Sopenharmony_ci	struct egpio_chip *egpio;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	egpio = gpiochip_get_data(chip);
20962306a36Sopenharmony_ci	if (test_bit(offset, &egpio->is_out)) {
21062306a36Sopenharmony_ci		egpio_set(chip, offset, value);
21162306a36Sopenharmony_ci		return 0;
21262306a36Sopenharmony_ci	} else {
21362306a36Sopenharmony_ci		return -EINVAL;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic int egpio_get_direction(struct gpio_chip *chip, unsigned offset)
21862306a36Sopenharmony_ci{
21962306a36Sopenharmony_ci	struct egpio_chip *egpio;
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	egpio = gpiochip_get_data(chip);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (test_bit(offset, &egpio->is_out))
22462306a36Sopenharmony_ci		return GPIO_LINE_DIRECTION_OUT;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	return GPIO_LINE_DIRECTION_IN;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic void egpio_write_cache(struct egpio_info *ei)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	int               i;
23262306a36Sopenharmony_ci	struct egpio_chip *egpio;
23362306a36Sopenharmony_ci	int               shift;
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	for (i = 0; i < ei->nchips; i++) {
23662306a36Sopenharmony_ci		egpio = &(ei->chip[i]);
23762306a36Sopenharmony_ci		if (!egpio->is_out)
23862306a36Sopenharmony_ci			continue;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci		for (shift = 0; shift < egpio->chip.ngpio;
24162306a36Sopenharmony_ci				shift += (1<<ei->reg_shift)) {
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci			int reg = egpio->reg_start + egpio_pos(ei, shift);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci			if (!((egpio->is_out >> shift) & ei->reg_mask))
24662306a36Sopenharmony_ci				continue;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci			pr_debug("EGPIO: setting %x to %x, was %x\n", reg,
24962306a36Sopenharmony_ci				(egpio->cached_values >> shift) & ei->reg_mask,
25062306a36Sopenharmony_ci				egpio_readw(ei, reg));
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci			egpio_writew((egpio->cached_values >> shift)
25362306a36Sopenharmony_ci					& ei->reg_mask, ei, reg);
25462306a36Sopenharmony_ci		}
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci/*
26062306a36Sopenharmony_ci * Setup
26162306a36Sopenharmony_ci */
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_cistatic int __init egpio_probe(struct platform_device *pdev)
26462306a36Sopenharmony_ci{
26562306a36Sopenharmony_ci	struct htc_egpio_platform_data *pdata = dev_get_platdata(&pdev->dev);
26662306a36Sopenharmony_ci	struct resource   *res;
26762306a36Sopenharmony_ci	struct egpio_info *ei;
26862306a36Sopenharmony_ci	struct gpio_chip  *chip;
26962306a36Sopenharmony_ci	unsigned int      irq, irq_end;
27062306a36Sopenharmony_ci	int               i;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	/* Initialize ei data structure. */
27362306a36Sopenharmony_ci	ei = devm_kzalloc(&pdev->dev, sizeof(*ei), GFP_KERNEL);
27462306a36Sopenharmony_ci	if (!ei)
27562306a36Sopenharmony_ci		return -ENOMEM;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	spin_lock_init(&ei->lock);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/* Find chained irq */
28062306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
28162306a36Sopenharmony_ci	if (res)
28262306a36Sopenharmony_ci		ei->chained_irq = res->start;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	/* Map egpio chip into virtual address space. */
28562306a36Sopenharmony_ci	ei->base_addr = devm_platform_ioremap_resource(pdev, 0);
28662306a36Sopenharmony_ci	if (IS_ERR(ei->base_addr))
28762306a36Sopenharmony_ci		return PTR_ERR(ei->base_addr);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	if ((pdata->bus_width != 16) && (pdata->bus_width != 32))
29062306a36Sopenharmony_ci		return -EINVAL;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	ei->bus_shift = fls(pdata->bus_width - 1) - 3;
29362306a36Sopenharmony_ci	pr_debug("bus_shift = %d\n", ei->bus_shift);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	if ((pdata->reg_width != 8) && (pdata->reg_width != 16))
29662306a36Sopenharmony_ci		return -EINVAL;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	ei->reg_shift = fls(pdata->reg_width - 1);
29962306a36Sopenharmony_ci	pr_debug("reg_shift = %d\n", ei->reg_shift);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	ei->reg_mask = (1 << pdata->reg_width) - 1;
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	platform_set_drvdata(pdev, ei);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	ei->nchips = pdata->num_chips;
30662306a36Sopenharmony_ci	ei->chip = devm_kcalloc(&pdev->dev,
30762306a36Sopenharmony_ci				ei->nchips, sizeof(struct egpio_chip),
30862306a36Sopenharmony_ci				GFP_KERNEL);
30962306a36Sopenharmony_ci	if (!ei->chip)
31062306a36Sopenharmony_ci		return -ENOMEM;
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	for (i = 0; i < ei->nchips; i++) {
31362306a36Sopenharmony_ci		ei->chip[i].reg_start = pdata->chip[i].reg_start;
31462306a36Sopenharmony_ci		ei->chip[i].cached_values = pdata->chip[i].initial_values;
31562306a36Sopenharmony_ci		ei->chip[i].is_out = pdata->chip[i].direction;
31662306a36Sopenharmony_ci		ei->chip[i].dev = &(pdev->dev);
31762306a36Sopenharmony_ci		chip = &(ei->chip[i].chip);
31862306a36Sopenharmony_ci		chip->label = devm_kasprintf(&pdev->dev, GFP_KERNEL,
31962306a36Sopenharmony_ci					     "htc-egpio-%d",
32062306a36Sopenharmony_ci					     i);
32162306a36Sopenharmony_ci		if (!chip->label)
32262306a36Sopenharmony_ci			return -ENOMEM;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci		chip->parent          = &pdev->dev;
32562306a36Sopenharmony_ci		chip->owner           = THIS_MODULE;
32662306a36Sopenharmony_ci		chip->get             = egpio_get;
32762306a36Sopenharmony_ci		chip->set             = egpio_set;
32862306a36Sopenharmony_ci		chip->direction_input = egpio_direction_input;
32962306a36Sopenharmony_ci		chip->direction_output = egpio_direction_output;
33062306a36Sopenharmony_ci		chip->get_direction   = egpio_get_direction;
33162306a36Sopenharmony_ci		chip->base            = pdata->chip[i].gpio_base;
33262306a36Sopenharmony_ci		chip->ngpio           = pdata->chip[i].num_gpios;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci		gpiochip_add_data(chip, &ei->chip[i]);
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	/* Set initial pin values */
33862306a36Sopenharmony_ci	egpio_write_cache(ei);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	ei->irq_start = pdata->irq_base;
34162306a36Sopenharmony_ci	ei->nirqs = pdata->num_irqs;
34262306a36Sopenharmony_ci	ei->ack_register = pdata->ack_register;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (ei->chained_irq) {
34562306a36Sopenharmony_ci		/* Setup irq handlers */
34662306a36Sopenharmony_ci		ei->ack_write = 0xFFFF;
34762306a36Sopenharmony_ci		if (pdata->invert_acks)
34862306a36Sopenharmony_ci			ei->ack_write = 0;
34962306a36Sopenharmony_ci		irq_end = ei->irq_start + ei->nirqs;
35062306a36Sopenharmony_ci		for (irq = ei->irq_start; irq < irq_end; irq++) {
35162306a36Sopenharmony_ci			irq_set_chip_and_handler(irq, &egpio_muxed_chip,
35262306a36Sopenharmony_ci						 handle_simple_irq);
35362306a36Sopenharmony_ci			irq_set_chip_data(irq, ei);
35462306a36Sopenharmony_ci			irq_clear_status_flags(irq, IRQ_NOREQUEST | IRQ_NOPROBE);
35562306a36Sopenharmony_ci		}
35662306a36Sopenharmony_ci		irq_set_irq_type(ei->chained_irq, IRQ_TYPE_EDGE_RISING);
35762306a36Sopenharmony_ci		irq_set_chained_handler_and_data(ei->chained_irq,
35862306a36Sopenharmony_ci						 egpio_handler, ei);
35962306a36Sopenharmony_ci		ack_irqs(ei);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci		device_init_wakeup(&pdev->dev, 1);
36262306a36Sopenharmony_ci	}
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	return 0;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci#ifdef CONFIG_PM
36862306a36Sopenharmony_cistatic int egpio_suspend(struct platform_device *pdev, pm_message_t state)
36962306a36Sopenharmony_ci{
37062306a36Sopenharmony_ci	struct egpio_info *ei = platform_get_drvdata(pdev);
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (ei->chained_irq && device_may_wakeup(&pdev->dev))
37362306a36Sopenharmony_ci		enable_irq_wake(ei->chained_irq);
37462306a36Sopenharmony_ci	return 0;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic int egpio_resume(struct platform_device *pdev)
37862306a36Sopenharmony_ci{
37962306a36Sopenharmony_ci	struct egpio_info *ei = platform_get_drvdata(pdev);
38062306a36Sopenharmony_ci
38162306a36Sopenharmony_ci	if (ei->chained_irq && device_may_wakeup(&pdev->dev))
38262306a36Sopenharmony_ci		disable_irq_wake(ei->chained_irq);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	/* Update registers from the cache, in case
38562306a36Sopenharmony_ci	   the CPLD was powered off during suspend */
38662306a36Sopenharmony_ci	egpio_write_cache(ei);
38762306a36Sopenharmony_ci	return 0;
38862306a36Sopenharmony_ci}
38962306a36Sopenharmony_ci#else
39062306a36Sopenharmony_ci#define egpio_suspend NULL
39162306a36Sopenharmony_ci#define egpio_resume NULL
39262306a36Sopenharmony_ci#endif
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic struct platform_driver egpio_driver = {
39662306a36Sopenharmony_ci	.driver = {
39762306a36Sopenharmony_ci		.name = "htc-egpio",
39862306a36Sopenharmony_ci		.suppress_bind_attrs = true,
39962306a36Sopenharmony_ci	},
40062306a36Sopenharmony_ci	.suspend      = egpio_suspend,
40162306a36Sopenharmony_ci	.resume       = egpio_resume,
40262306a36Sopenharmony_ci};
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_cistatic int __init egpio_init(void)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	return platform_driver_probe(&egpio_driver, egpio_probe);
40762306a36Sopenharmony_ci}
40862306a36Sopenharmony_ci/* start early for dependencies */
40962306a36Sopenharmony_cisubsys_initcall(egpio_init);
410