linux/drivers/gpio/gpio-lpc18xx.c
<<
>>
Prefs
   1/*
   2 * GPIO driver for NXP LPC18xx/43xx.
   3 *
   4 * Copyright (C) 2015 Joachim Eastwood <manabian@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License version 2 as
   8 * published by the Free Software Foundation.
   9 *
  10 */
  11
  12#include <linux/clk.h>
  13#include <linux/gpio/driver.h>
  14#include <linux/io.h>
  15#include <linux/module.h>
  16#include <linux/of.h>
  17#include <linux/of_gpio.h>
  18#include <linux/pinctrl/consumer.h>
  19#include <linux/platform_device.h>
  20
  21/* LPC18xx GPIO register offsets */
  22#define LPC18XX_REG_DIR(n)      (0x2000 + n * sizeof(u32))
  23
  24#define LPC18XX_MAX_PORTS       8
  25#define LPC18XX_PINS_PER_PORT   32
  26
  27struct lpc18xx_gpio_chip {
  28        struct gpio_chip gpio;
  29        void __iomem *base;
  30        struct clk *clk;
  31        spinlock_t lock;
  32};
  33
  34static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
  35{
  36        struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip);
  37        writeb(value ? 1 : 0, gc->base + offset);
  38}
  39
  40static int lpc18xx_gpio_get(struct gpio_chip *chip, unsigned offset)
  41{
  42        struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip);
  43        return !!readb(gc->base + offset);
  44}
  45
  46static int lpc18xx_gpio_direction(struct gpio_chip *chip, unsigned offset,
  47                                  bool out)
  48{
  49        struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip);
  50        unsigned long flags;
  51        u32 port, pin, dir;
  52
  53        port = offset / LPC18XX_PINS_PER_PORT;
  54        pin  = offset % LPC18XX_PINS_PER_PORT;
  55
  56        spin_lock_irqsave(&gc->lock, flags);
  57        dir = readl(gc->base + LPC18XX_REG_DIR(port));
  58        if (out)
  59                dir |= BIT(pin);
  60        else
  61                dir &= ~BIT(pin);
  62        writel(dir, gc->base + LPC18XX_REG_DIR(port));
  63        spin_unlock_irqrestore(&gc->lock, flags);
  64
  65        return 0;
  66}
  67
  68static int lpc18xx_gpio_direction_input(struct gpio_chip *chip,
  69                                        unsigned offset)
  70{
  71        return lpc18xx_gpio_direction(chip, offset, false);
  72}
  73
  74static int lpc18xx_gpio_direction_output(struct gpio_chip *chip,
  75                                         unsigned offset, int value)
  76{
  77        lpc18xx_gpio_set(chip, offset, value);
  78        return lpc18xx_gpio_direction(chip, offset, true);
  79}
  80
  81static const struct gpio_chip lpc18xx_chip = {
  82        .label                  = "lpc18xx/43xx-gpio",
  83        .request                = gpiochip_generic_request,
  84        .free                   = gpiochip_generic_free,
  85        .direction_input        = lpc18xx_gpio_direction_input,
  86        .direction_output       = lpc18xx_gpio_direction_output,
  87        .set                    = lpc18xx_gpio_set,
  88        .get                    = lpc18xx_gpio_get,
  89        .ngpio                  = LPC18XX_MAX_PORTS * LPC18XX_PINS_PER_PORT,
  90        .owner                  = THIS_MODULE,
  91};
  92
  93static int lpc18xx_gpio_probe(struct platform_device *pdev)
  94{
  95        struct lpc18xx_gpio_chip *gc;
  96        struct resource *res;
  97        int ret;
  98
  99        gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
 100        if (!gc)
 101                return -ENOMEM;
 102
 103        gc->gpio = lpc18xx_chip;
 104        platform_set_drvdata(pdev, gc);
 105
 106        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 107        gc->base = devm_ioremap_resource(&pdev->dev, res);
 108        if (IS_ERR(gc->base))
 109                return PTR_ERR(gc->base);
 110
 111        gc->clk = devm_clk_get(&pdev->dev, NULL);
 112        if (IS_ERR(gc->clk)) {
 113                dev_err(&pdev->dev, "input clock not found\n");
 114                return PTR_ERR(gc->clk);
 115        }
 116
 117        ret = clk_prepare_enable(gc->clk);
 118        if (ret) {
 119                dev_err(&pdev->dev, "unable to enable clock\n");
 120                return ret;
 121        }
 122
 123        spin_lock_init(&gc->lock);
 124
 125        gc->gpio.parent = &pdev->dev;
 126
 127        ret = gpiochip_add_data(&gc->gpio, gc);
 128        if (ret) {
 129                dev_err(&pdev->dev, "failed to add gpio chip\n");
 130                clk_disable_unprepare(gc->clk);
 131                return ret;
 132        }
 133
 134        return 0;
 135}
 136
 137static int lpc18xx_gpio_remove(struct platform_device *pdev)
 138{
 139        struct lpc18xx_gpio_chip *gc = platform_get_drvdata(pdev);
 140
 141        gpiochip_remove(&gc->gpio);
 142        clk_disable_unprepare(gc->clk);
 143
 144        return 0;
 145}
 146
 147static const struct of_device_id lpc18xx_gpio_match[] = {
 148        { .compatible = "nxp,lpc1850-gpio" },
 149        { }
 150};
 151MODULE_DEVICE_TABLE(of, lpc18xx_gpio_match);
 152
 153static struct platform_driver lpc18xx_gpio_driver = {
 154        .probe  = lpc18xx_gpio_probe,
 155        .remove = lpc18xx_gpio_remove,
 156        .driver = {
 157                .name           = "lpc18xx-gpio",
 158                .of_match_table = lpc18xx_gpio_match,
 159        },
 160};
 161module_platform_driver(lpc18xx_gpio_driver);
 162
 163MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");
 164MODULE_DESCRIPTION("GPIO driver for LPC18xx/43xx");
 165MODULE_LICENSE("GPL v2");
 166