gpio-lpc18xx.c 3.97 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/*
 * GPIO driver for NXP LPC18xx/43xx.
 *
 * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 *
 */

#include <linux/clk.h>
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/pinctrl/consumer.h>
#include <linux/platform_device.h>

/* LPC18xx GPIO register offsets */
#define LPC18XX_REG_DIR(n)	(0x2000 + n * sizeof(u32))

#define LPC18XX_MAX_PORTS	8
#define LPC18XX_PINS_PER_PORT	32

struct lpc18xx_gpio_chip {
	struct gpio_chip gpio;
	void __iomem *base;
	struct clk *clk;
	spinlock_t lock;
};

static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
36
	struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip);
37 38 39 40 41
	writeb(value ? 1 : 0, gc->base + offset);
}

static int lpc18xx_gpio_get(struct gpio_chip *chip, unsigned offset)
{
42
	struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip);
43 44 45 46 47 48
	return !!readb(gc->base + offset);
}

static int lpc18xx_gpio_direction(struct gpio_chip *chip, unsigned offset,
				  bool out)
{
49
	struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip);
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
	unsigned long flags;
	u32 port, pin, dir;

	port = offset / LPC18XX_PINS_PER_PORT;
	pin  = offset % LPC18XX_PINS_PER_PORT;

	spin_lock_irqsave(&gc->lock, flags);
	dir = readl(gc->base + LPC18XX_REG_DIR(port));
	if (out)
		dir |= BIT(pin);
	else
		dir &= ~BIT(pin);
	writel(dir, gc->base + LPC18XX_REG_DIR(port));
	spin_unlock_irqrestore(&gc->lock, flags);

	return 0;
}

static int lpc18xx_gpio_direction_input(struct gpio_chip *chip,
					unsigned offset)
{
	return lpc18xx_gpio_direction(chip, offset, false);
}

static int lpc18xx_gpio_direction_output(struct gpio_chip *chip,
					 unsigned offset, int value)
{
	lpc18xx_gpio_set(chip, offset, value);
	return lpc18xx_gpio_direction(chip, offset, true);
}

81
static const struct gpio_chip lpc18xx_chip = {
82
	.label			= "lpc18xx/43xx-gpio",
83 84
	.request		= gpiochip_generic_request,
	.free			= gpiochip_generic_free,
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
	.direction_input	= lpc18xx_gpio_direction_input,
	.direction_output	= lpc18xx_gpio_direction_output,
	.set			= lpc18xx_gpio_set,
	.get			= lpc18xx_gpio_get,
	.ngpio			= LPC18XX_MAX_PORTS * LPC18XX_PINS_PER_PORT,
	.owner			= THIS_MODULE,
};

static int lpc18xx_gpio_probe(struct platform_device *pdev)
{
	struct lpc18xx_gpio_chip *gc;
	struct resource *res;
	int ret;

	gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
	if (!gc)
		return -ENOMEM;

	gc->gpio = lpc18xx_chip;
	platform_set_drvdata(pdev, gc);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	gc->base = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(gc->base))
		return PTR_ERR(gc->base);

	gc->clk = devm_clk_get(&pdev->dev, NULL);
	if (IS_ERR(gc->clk)) {
		dev_err(&pdev->dev, "input clock not found\n");
		return PTR_ERR(gc->clk);
	}

	ret = clk_prepare_enable(gc->clk);
	if (ret) {
		dev_err(&pdev->dev, "unable to enable clock\n");
		return ret;
	}

	spin_lock_init(&gc->lock);

125
	gc->gpio.parent = &pdev->dev;
126

127
	ret = gpiochip_add_data(&gc->gpio, gc);
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
	if (ret) {
		dev_err(&pdev->dev, "failed to add gpio chip\n");
		clk_disable_unprepare(gc->clk);
		return ret;
	}

	return 0;
}

static int lpc18xx_gpio_remove(struct platform_device *pdev)
{
	struct lpc18xx_gpio_chip *gc = platform_get_drvdata(pdev);

	gpiochip_remove(&gc->gpio);
	clk_disable_unprepare(gc->clk);

	return 0;
}

static const struct of_device_id lpc18xx_gpio_match[] = {
	{ .compatible = "nxp,lpc1850-gpio" },
	{ }
};
MODULE_DEVICE_TABLE(of, lpc18xx_gpio_match);

static struct platform_driver lpc18xx_gpio_driver = {
	.probe	= lpc18xx_gpio_probe,
	.remove	= lpc18xx_gpio_remove,
	.driver	= {
		.name		= "lpc18xx-gpio",
		.of_match_table	= lpc18xx_gpio_match,
	},
};
module_platform_driver(lpc18xx_gpio_driver);

MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
MODULE_DESCRIPTION("GPIO driver for LPC18xx/43xx");
MODULE_LICENSE("GPL v2");