Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb485c7d authored by Linus Walleij's avatar Linus Walleij
Browse files

Merge branch 'pl061' into devel

parents 46a5c112 6da7b0dd
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@
#include <linux/amba/bus.h>
#include <linux/amba/clcd.h>
#include <linux/amba/mmci.h>
#include <linux/amba/pl061.h>
#include <linux/io.h>
#include <linux/platform_data/clk-integrator.h>
#include <linux/slab.h>
+94 −114
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/amba/bus.h>
#include <linux/amba/pl061.h>
#include <linux/slab.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm.h>
@@ -50,11 +49,12 @@ struct pl061_context_save_regs {
};
#endif

struct pl061_gpio {
struct pl061 {
	spinlock_t		lock;

	void __iomem		*base;
	struct gpio_chip	gc;
	int			parent_irq;

#ifdef CONFIG_PM
	struct pl061_context_save_regs csave_regs;
@@ -63,22 +63,22 @@ struct pl061_gpio {

static int pl061_get_direction(struct gpio_chip *gc, unsigned offset)
{
	struct pl061_gpio *chip = gpiochip_get_data(gc);
	struct pl061 *pl061 = gpiochip_get_data(gc);

	return !(readb(chip->base + GPIODIR) & BIT(offset));
	return !(readb(pl061->base + GPIODIR) & BIT(offset));
}

static int pl061_direction_input(struct gpio_chip *gc, unsigned offset)
{
	struct pl061_gpio *chip = gpiochip_get_data(gc);
	struct pl061 *pl061 = gpiochip_get_data(gc);
	unsigned long flags;
	unsigned char gpiodir;

	spin_lock_irqsave(&chip->lock, flags);
	gpiodir = readb(chip->base + GPIODIR);
	spin_lock_irqsave(&pl061->lock, flags);
	gpiodir = readb(pl061->base + GPIODIR);
	gpiodir &= ~(BIT(offset));
	writeb(gpiodir, chip->base + GPIODIR);
	spin_unlock_irqrestore(&chip->lock, flags);
	writeb(gpiodir, pl061->base + GPIODIR);
	spin_unlock_irqrestore(&pl061->lock, flags);

	return 0;
}
@@ -86,44 +86,44 @@ static int pl061_direction_input(struct gpio_chip *gc, unsigned offset)
static int pl061_direction_output(struct gpio_chip *gc, unsigned offset,
		int value)
{
	struct pl061_gpio *chip = gpiochip_get_data(gc);
	struct pl061 *pl061 = gpiochip_get_data(gc);
	unsigned long flags;
	unsigned char gpiodir;

	spin_lock_irqsave(&chip->lock, flags);
	writeb(!!value << offset, chip->base + (BIT(offset + 2)));
	gpiodir = readb(chip->base + GPIODIR);
	spin_lock_irqsave(&pl061->lock, flags);
	writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
	gpiodir = readb(pl061->base + GPIODIR);
	gpiodir |= BIT(offset);
	writeb(gpiodir, chip->base + GPIODIR);
	writeb(gpiodir, pl061->base + GPIODIR);

	/*
	 * gpio value is set again, because pl061 doesn't allow to set value of
	 * a gpio pin before configuring it in OUT mode.
	 */
	writeb(!!value << offset, chip->base + (BIT(offset + 2)));
	spin_unlock_irqrestore(&chip->lock, flags);
	writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
	spin_unlock_irqrestore(&pl061->lock, flags);

	return 0;
}

static int pl061_get_value(struct gpio_chip *gc, unsigned offset)
{
	struct pl061_gpio *chip = gpiochip_get_data(gc);
	struct pl061 *pl061 = gpiochip_get_data(gc);

	return !!readb(chip->base + (BIT(offset + 2)));
	return !!readb(pl061->base + (BIT(offset + 2)));
}

static void pl061_set_value(struct gpio_chip *gc, unsigned offset, int value)
{
	struct pl061_gpio *chip = gpiochip_get_data(gc);
	struct pl061 *pl061 = gpiochip_get_data(gc);

	writeb(!!value << offset, chip->base + (BIT(offset + 2)));
	writeb(!!value << offset, pl061->base + (BIT(offset + 2)));
}

static int pl061_irq_type(struct irq_data *d, unsigned trigger)
{
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct pl061_gpio *chip = gpiochip_get_data(gc);
	struct pl061 *pl061 = gpiochip_get_data(gc);
	int offset = irqd_to_hwirq(d);
	unsigned long flags;
	u8 gpiois, gpioibe, gpioiev;
@@ -143,11 +143,11 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
	}


	spin_lock_irqsave(&chip->lock, flags);
	spin_lock_irqsave(&pl061->lock, flags);

	gpioiev = readb(chip->base + GPIOIEV);
	gpiois = readb(chip->base + GPIOIS);
	gpioibe = readb(chip->base + GPIOIBE);
	gpioiev = readb(pl061->base + GPIOIEV);
	gpiois = readb(pl061->base + GPIOIS);
	gpioibe = readb(pl061->base + GPIOIBE);

	if (trigger & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) {
		bool polarity = trigger & IRQ_TYPE_LEVEL_HIGH;
@@ -199,11 +199,11 @@ static int pl061_irq_type(struct irq_data *d, unsigned trigger)
			 offset);
	}

	writeb(gpiois, chip->base + GPIOIS);
	writeb(gpioibe, chip->base + GPIOIBE);
	writeb(gpioiev, chip->base + GPIOIEV);
	writeb(gpiois, pl061->base + GPIOIS);
	writeb(gpioibe, pl061->base + GPIOIBE);
	writeb(gpioiev, pl061->base + GPIOIEV);

	spin_unlock_irqrestore(&chip->lock, flags);
	spin_unlock_irqrestore(&pl061->lock, flags);

	return 0;
}
@@ -213,12 +213,12 @@ static void pl061_irq_handler(struct irq_desc *desc)
	unsigned long pending;
	int offset;
	struct gpio_chip *gc = irq_desc_get_handler_data(desc);
	struct pl061_gpio *chip = gpiochip_get_data(gc);
	struct pl061 *pl061 = gpiochip_get_data(gc);
	struct irq_chip *irqchip = irq_desc_get_chip(desc);

	chained_irq_enter(irqchip, desc);

	pending = readb(chip->base + GPIOMIS);
	pending = readb(pl061->base + GPIOMIS);
	if (pending) {
		for_each_set_bit(offset, &pending, PL061_GPIO_NR)
			generic_handle_irq(irq_find_mapping(gc->irqdomain,
@@ -231,27 +231,27 @@ static void pl061_irq_handler(struct irq_desc *desc)
static void pl061_irq_mask(struct irq_data *d)
{
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct pl061_gpio *chip = gpiochip_get_data(gc);
	struct pl061 *pl061 = gpiochip_get_data(gc);
	u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
	u8 gpioie;

	spin_lock(&chip->lock);
	gpioie = readb(chip->base + GPIOIE) & ~mask;
	writeb(gpioie, chip->base + GPIOIE);
	spin_unlock(&chip->lock);
	spin_lock(&pl061->lock);
	gpioie = readb(pl061->base + GPIOIE) & ~mask;
	writeb(gpioie, pl061->base + GPIOIE);
	spin_unlock(&pl061->lock);
}

static void pl061_irq_unmask(struct irq_data *d)
{
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct pl061_gpio *chip = gpiochip_get_data(gc);
	struct pl061 *pl061 = gpiochip_get_data(gc);
	u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);
	u8 gpioie;

	spin_lock(&chip->lock);
	gpioie = readb(chip->base + GPIOIE) | mask;
	writeb(gpioie, chip->base + GPIOIE);
	spin_unlock(&chip->lock);
	spin_lock(&pl061->lock);
	gpioie = readb(pl061->base + GPIOIE) | mask;
	writeb(gpioie, pl061->base + GPIOIE);
	spin_unlock(&pl061->lock);
}

/**
@@ -265,19 +265,20 @@ static void pl061_irq_unmask(struct irq_data *d)
static void pl061_irq_ack(struct irq_data *d)
{
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct pl061_gpio *chip = gpiochip_get_data(gc);
	struct pl061 *pl061 = gpiochip_get_data(gc);
	u8 mask = BIT(irqd_to_hwirq(d) % PL061_GPIO_NR);

	spin_lock(&chip->lock);
	writeb(mask, chip->base + GPIOIC);
	spin_unlock(&chip->lock);
	spin_lock(&pl061->lock);
	writeb(mask, pl061->base + GPIOIC);
	spin_unlock(&pl061->lock);
}

static int pl061_irq_set_wake(struct irq_data *d, unsigned int state)
{
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct pl061 *pl061 = gpiochip_get_data(gc);

	return irq_set_irq_wake(gc->irq_parent, state);
	return irq_set_irq_wake(pl061->parent_irq, state);
}

static struct irq_chip pl061_irqchip = {
@@ -292,81 +293,60 @@ static struct irq_chip pl061_irqchip = {
static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
{
	struct device *dev = &adev->dev;
	struct pl061_platform_data *pdata = dev_get_platdata(dev);
	struct pl061_gpio *chip;
	int ret, irq, i, irq_base;
	struct pl061 *pl061;
	int ret, irq;

	chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
	if (chip == NULL)
	pl061 = devm_kzalloc(dev, sizeof(*pl061), GFP_KERNEL);
	if (pl061 == NULL)
		return -ENOMEM;

	if (pdata) {
		chip->gc.base = pdata->gpio_base;
		irq_base = pdata->irq_base;
		if (irq_base <= 0) {
			dev_err(&adev->dev, "invalid IRQ base in pdata\n");
			return -ENODEV;
		}
	} else {
		chip->gc.base = -1;
		irq_base = 0;
	}

	chip->base = devm_ioremap_resource(dev, &adev->res);
	if (IS_ERR(chip->base))
		return PTR_ERR(chip->base);
	pl061->base = devm_ioremap_resource(dev, &adev->res);
	if (IS_ERR(pl061->base))
		return PTR_ERR(pl061->base);

	spin_lock_init(&chip->lock);
	spin_lock_init(&pl061->lock);
	if (of_property_read_bool(dev->of_node, "gpio-ranges")) {
		chip->gc.request = gpiochip_generic_request;
		chip->gc.free = gpiochip_generic_free;
		pl061->gc.request = gpiochip_generic_request;
		pl061->gc.free = gpiochip_generic_free;
	}

	chip->gc.get_direction = pl061_get_direction;
	chip->gc.direction_input = pl061_direction_input;
	chip->gc.direction_output = pl061_direction_output;
	chip->gc.get = pl061_get_value;
	chip->gc.set = pl061_set_value;
	chip->gc.ngpio = PL061_GPIO_NR;
	chip->gc.label = dev_name(dev);
	chip->gc.parent = dev;
	chip->gc.owner = THIS_MODULE;

	ret = gpiochip_add_data(&chip->gc, chip);
	pl061->gc.base = -1;
	pl061->gc.get_direction = pl061_get_direction;
	pl061->gc.direction_input = pl061_direction_input;
	pl061->gc.direction_output = pl061_direction_output;
	pl061->gc.get = pl061_get_value;
	pl061->gc.set = pl061_set_value;
	pl061->gc.ngpio = PL061_GPIO_NR;
	pl061->gc.label = dev_name(dev);
	pl061->gc.parent = dev;
	pl061->gc.owner = THIS_MODULE;

	ret = gpiochip_add_data(&pl061->gc, pl061);
	if (ret)
		return ret;

	/*
	 * irq_chip support
	 */
	writeb(0, chip->base + GPIOIE); /* disable irqs */
	writeb(0, pl061->base + GPIOIE); /* disable irqs */
	irq = adev->irq[0];
	if (irq < 0) {
		dev_err(&adev->dev, "invalid IRQ\n");
		return -ENODEV;
	}
	pl061->parent_irq = irq;

	ret = gpiochip_irqchip_add(&chip->gc, &pl061_irqchip,
				   irq_base, handle_bad_irq,
	ret = gpiochip_irqchip_add(&pl061->gc, &pl061_irqchip,
				   0, handle_bad_irq,
				   IRQ_TYPE_NONE);
	if (ret) {
		dev_info(&adev->dev, "could not add irqchip\n");
		return ret;
	}
	gpiochip_set_chained_irqchip(&chip->gc, &pl061_irqchip,
	gpiochip_set_chained_irqchip(&pl061->gc, &pl061_irqchip,
				     irq, pl061_irq_handler);

	for (i = 0; i < PL061_GPIO_NR; i++) {
		if (pdata) {
			if (pdata->directions & (BIT(i)))
				pl061_direction_output(&chip->gc, i,
						pdata->values & (BIT(i)));
			else
				pl061_direction_input(&chip->gc, i);
		}
	}

	amba_set_drvdata(adev, chip);
	amba_set_drvdata(adev, pl061);
	dev_info(&adev->dev, "PL061 GPIO chip @%pa registered\n",
		 &adev->res.start);

@@ -376,20 +356,20 @@ static int pl061_probe(struct amba_device *adev, const struct amba_id *id)
#ifdef CONFIG_PM
static int pl061_suspend(struct device *dev)
{
	struct pl061_gpio *chip = dev_get_drvdata(dev);
	struct pl061 *pl061 = dev_get_drvdata(dev);
	int offset;

	chip->csave_regs.gpio_data = 0;
	chip->csave_regs.gpio_dir = readb(chip->base + GPIODIR);
	chip->csave_regs.gpio_is = readb(chip->base + GPIOIS);
	chip->csave_regs.gpio_ibe = readb(chip->base + GPIOIBE);
	chip->csave_regs.gpio_iev = readb(chip->base + GPIOIEV);
	chip->csave_regs.gpio_ie = readb(chip->base + GPIOIE);
	pl061->csave_regs.gpio_data = 0;
	pl061->csave_regs.gpio_dir = readb(pl061->base + GPIODIR);
	pl061->csave_regs.gpio_is = readb(pl061->base + GPIOIS);
	pl061->csave_regs.gpio_ibe = readb(pl061->base + GPIOIBE);
	pl061->csave_regs.gpio_iev = readb(pl061->base + GPIOIEV);
	pl061->csave_regs.gpio_ie = readb(pl061->base + GPIOIE);

	for (offset = 0; offset < PL061_GPIO_NR; offset++) {
		if (chip->csave_regs.gpio_dir & (BIT(offset)))
			chip->csave_regs.gpio_data |=
				pl061_get_value(&chip->gc, offset) << offset;
		if (pl061->csave_regs.gpio_dir & (BIT(offset)))
			pl061->csave_regs.gpio_data |=
				pl061_get_value(&pl061->gc, offset) << offset;
	}

	return 0;
@@ -397,22 +377,22 @@ static int pl061_suspend(struct device *dev)

static int pl061_resume(struct device *dev)
{
	struct pl061_gpio *chip = dev_get_drvdata(dev);
	struct pl061 *pl061 = dev_get_drvdata(dev);
	int offset;

	for (offset = 0; offset < PL061_GPIO_NR; offset++) {
		if (chip->csave_regs.gpio_dir & (BIT(offset)))
			pl061_direction_output(&chip->gc, offset,
					chip->csave_regs.gpio_data &
		if (pl061->csave_regs.gpio_dir & (BIT(offset)))
			pl061_direction_output(&pl061->gc, offset,
					pl061->csave_regs.gpio_data &
					(BIT(offset)));
		else
			pl061_direction_input(&chip->gc, offset);
			pl061_direction_input(&pl061->gc, offset);
	}

	writeb(chip->csave_regs.gpio_is, chip->base + GPIOIS);
	writeb(chip->csave_regs.gpio_ibe, chip->base + GPIOIBE);
	writeb(chip->csave_regs.gpio_iev, chip->base + GPIOIEV);
	writeb(chip->csave_regs.gpio_ie, chip->base + GPIOIE);
	writeb(pl061->csave_regs.gpio_is, pl061->base + GPIOIS);
	writeb(pl061->csave_regs.gpio_ibe, pl061->base + GPIOIBE);
	writeb(pl061->csave_regs.gpio_iev, pl061->base + GPIOIEV);
	writeb(pl061->csave_regs.gpio_ie, pl061->base + GPIOIE);

	return 0;
}

include/linux/amba/pl061.h

deleted100644 → 0
+0 −16
Original line number Diff line number Diff line
#include <linux/types.h>

/* platform data for the PL061 GPIO driver */

struct pl061_platform_data {
	/* number of the first GPIO */
	unsigned	gpio_base;

	/* number of the first IRQ.
	 * If the IRQ functionality in not desired this must be set to 0.
	 */
	unsigned	irq_base;

	u8		directions;	/* startup directions, 1: out, 0: in */
	u8		values;		/* startup values */
};