linux/drivers/gpio/gpio-loongson.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Loongson-2F/3A/3B GPIO Support
   4 *
   5 *  Copyright (c) 2008 Richard Liu,  STMicroelectronics  <richard.liu@st.com>
   6 *  Copyright (c) 2008-2010 Arnaud Patard <apatard@mandriva.com>
   7 *  Copyright (c) 2013 Hongbing Hu <huhb@lemote.com>
   8 *  Copyright (c) 2014 Huacai Chen <chenhc@lemote.com>
   9 */
  10
  11#include <linux/kernel.h>
  12#include <linux/init.h>
  13#include <linux/module.h>
  14#include <linux/spinlock.h>
  15#include <linux/err.h>
  16#include <linux/gpio/driver.h>
  17#include <linux/platform_device.h>
  18#include <linux/bitops.h>
  19#include <asm/types.h>
  20#include <loongson.h>
  21
  22#define STLS2F_N_GPIO           4
  23#define STLS3A_N_GPIO           16
  24
  25#ifdef CONFIG_CPU_LOONGSON64
  26#define LOONGSON_N_GPIO STLS3A_N_GPIO
  27#else
  28#define LOONGSON_N_GPIO STLS2F_N_GPIO
  29#endif
  30
  31/*
  32 * Offset into the register where we read lines, we write them from offset 0.
  33 * This offset is the only thing that stand between us and using
  34 * GPIO_GENERIC.
  35 */
  36#define LOONGSON_GPIO_IN_OFFSET 16
  37
  38static DEFINE_SPINLOCK(gpio_lock);
  39
  40static int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
  41{
  42        u32 val;
  43
  44        spin_lock(&gpio_lock);
  45        val = LOONGSON_GPIODATA;
  46        spin_unlock(&gpio_lock);
  47
  48        return !!(val & BIT(gpio + LOONGSON_GPIO_IN_OFFSET));
  49}
  50
  51static void loongson_gpio_set_value(struct gpio_chip *chip,
  52                unsigned gpio, int value)
  53{
  54        u32 val;
  55
  56        spin_lock(&gpio_lock);
  57        val = LOONGSON_GPIODATA;
  58        if (value)
  59                val |= BIT(gpio);
  60        else
  61                val &= ~BIT(gpio);
  62        LOONGSON_GPIODATA = val;
  63        spin_unlock(&gpio_lock);
  64}
  65
  66static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
  67{
  68        u32 temp;
  69
  70        spin_lock(&gpio_lock);
  71        temp = LOONGSON_GPIOIE;
  72        temp |= BIT(gpio);
  73        LOONGSON_GPIOIE = temp;
  74        spin_unlock(&gpio_lock);
  75
  76        return 0;
  77}
  78
  79static int loongson_gpio_direction_output(struct gpio_chip *chip,
  80                unsigned gpio, int level)
  81{
  82        u32 temp;
  83
  84        loongson_gpio_set_value(chip, gpio, level);
  85        spin_lock(&gpio_lock);
  86        temp = LOONGSON_GPIOIE;
  87        temp &= ~BIT(gpio);
  88        LOONGSON_GPIOIE = temp;
  89        spin_unlock(&gpio_lock);
  90
  91        return 0;
  92}
  93
  94static int loongson_gpio_probe(struct platform_device *pdev)
  95{
  96        struct gpio_chip *gc;
  97        struct device *dev = &pdev->dev;
  98
  99        gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL);
 100        if (!gc)
 101                return -ENOMEM;
 102
 103        gc->label = "loongson-gpio-chip";
 104        gc->base = 0;
 105        gc->ngpio = LOONGSON_N_GPIO;
 106        gc->get = loongson_gpio_get_value;
 107        gc->set = loongson_gpio_set_value;
 108        gc->direction_input = loongson_gpio_direction_input;
 109        gc->direction_output = loongson_gpio_direction_output;
 110
 111        return gpiochip_add_data(gc, NULL);
 112}
 113
 114static struct platform_driver loongson_gpio_driver = {
 115        .driver = {
 116                .name = "loongson-gpio",
 117        },
 118        .probe = loongson_gpio_probe,
 119};
 120
 121static int __init loongson_gpio_setup(void)
 122{
 123        struct platform_device *pdev;
 124        int ret;
 125
 126        ret = platform_driver_register(&loongson_gpio_driver);
 127        if (ret) {
 128                pr_err("error registering loongson GPIO driver\n");
 129                return ret;
 130        }
 131
 132        pdev = platform_device_register_simple("loongson-gpio", -1, NULL, 0);
 133        return PTR_ERR_OR_ZERO(pdev);
 134}
 135postcore_initcall(loongson_gpio_setup);
 136