blob: 00943170ce369a00ae2727a9576af58557ef4d1f [file] [log] [blame]
Thomas Gleixner2874c5f2019-05-27 08:55:01 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Arnaud Patardc197da92010-04-29 11:58:54 +02002/*
Huacai Chencbfb3ea2015-04-01 10:20:09 +08003 * Loongson-2F/3A/3B GPIO Support
Arnaud Patardc197da92010-04-29 11:58:54 +02004 *
Ralf Baechle70342282013-01-22 12:59:30 +01005 * Copyright (c) 2008 Richard Liu, STMicroelectronics <richard.liu@st.com>
Arnaud Patardc197da92010-04-29 11:58:54 +02006 * Copyright (c) 2008-2010 Arnaud Patard <apatard@mandriva.com>
Huacai Chencbfb3ea2015-04-01 10:20:09 +08007 * Copyright (c) 2013 Hongbing Hu <huhb@lemote.com>
8 * Copyright (c) 2014 Huacai Chen <chenhc@lemote.com>
Arnaud Patardc197da92010-04-29 11:58:54 +02009 */
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>
Linus Walleijf105edf2018-04-13 10:28:21 +020016#include <linux/gpio/driver.h>
Linus Walleij70e703e2018-04-13 12:05:36 +020017#include <linux/platform_device.h>
Linus Walleija4e5db82018-04-13 12:20:24 +020018#include <linux/bitops.h>
Arnaud Patardc197da92010-04-29 11:58:54 +020019#include <asm/types.h>
20#include <loongson.h>
Arnaud Patardc197da92010-04-29 11:58:54 +020021
22#define STLS2F_N_GPIO 4
Huacai Chencbfb3ea2015-04-01 10:20:09 +080023#define STLS3A_N_GPIO 16
24
25#ifdef CONFIG_CPU_LOONGSON3
26#define LOONGSON_N_GPIO STLS3A_N_GPIO
27#else
28#define LOONGSON_N_GPIO STLS2F_N_GPIO
29#endif
30
Linus Walleija4e5db82018-04-13 12:20:24 +020031/*
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 */
Huacai Chencbfb3ea2015-04-01 10:20:09 +080036#define LOONGSON_GPIO_IN_OFFSET 16
Arnaud Patardc197da92010-04-29 11:58:54 +020037
38static DEFINE_SPINLOCK(gpio_lock);
39
Huacai Chencbfb3ea2015-04-01 10:20:09 +080040static int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio)
Arnaud Patardc197da92010-04-29 11:58:54 +020041{
Huacai Chendf5dade2015-04-01 10:20:07 +080042 u32 val;
Huacai Chendf5dade2015-04-01 10:20:07 +080043
Huacai Chendf5dade2015-04-01 10:20:07 +080044 spin_lock(&gpio_lock);
45 val = LOONGSON_GPIODATA;
46 spin_unlock(&gpio_lock);
47
Linus Walleija4e5db82018-04-13 12:20:24 +020048 return !!(val & BIT(gpio + LOONGSON_GPIO_IN_OFFSET));
Arnaud Patardc197da92010-04-29 11:58:54 +020049}
50
Huacai Chencbfb3ea2015-04-01 10:20:09 +080051static void loongson_gpio_set_value(struct gpio_chip *chip,
Arnaud Patardc197da92010-04-29 11:58:54 +020052 unsigned gpio, int value)
53{
Huacai Chendf5dade2015-04-01 10:20:07 +080054 u32 val;
Huacai Chendf5dade2015-04-01 10:20:07 +080055
56 spin_lock(&gpio_lock);
57 val = LOONGSON_GPIODATA;
58 if (value)
Linus Walleija4e5db82018-04-13 12:20:24 +020059 val |= BIT(gpio);
Huacai Chendf5dade2015-04-01 10:20:07 +080060 else
Linus Walleija4e5db82018-04-13 12:20:24 +020061 val &= ~BIT(gpio);
Huacai Chendf5dade2015-04-01 10:20:07 +080062 LOONGSON_GPIODATA = val;
63 spin_unlock(&gpio_lock);
Arnaud Patardc197da92010-04-29 11:58:54 +020064}
65
Linus Walleijf105edf2018-04-13 10:28:21 +020066static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
67{
68 u32 temp;
Linus Walleijf105edf2018-04-13 10:28:21 +020069
70 spin_lock(&gpio_lock);
Linus Walleijf105edf2018-04-13 10:28:21 +020071 temp = LOONGSON_GPIOIE;
Linus Walleija4e5db82018-04-13 12:20:24 +020072 temp |= BIT(gpio);
Linus Walleijf105edf2018-04-13 10:28:21 +020073 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;
Linus Walleijf105edf2018-04-13 10:28:21 +020083
84 loongson_gpio_set_value(chip, gpio, level);
85 spin_lock(&gpio_lock);
Linus Walleijf105edf2018-04-13 10:28:21 +020086 temp = LOONGSON_GPIOIE;
Linus Walleija4e5db82018-04-13 12:20:24 +020087 temp &= ~BIT(gpio);
Linus Walleijf105edf2018-04-13 10:28:21 +020088 LOONGSON_GPIOIE = temp;
89 spin_unlock(&gpio_lock);
90
91 return 0;
92}
93
Linus Walleij70e703e2018-04-13 12:05:36 +020094static 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,
Arnaud Patardc197da92010-04-29 11:58:54 +0200119};
120
Huacai Chencbfb3ea2015-04-01 10:20:09 +0800121static int __init loongson_gpio_setup(void)
Arnaud Patardc197da92010-04-29 11:58:54 +0200122{
Linus Walleij70e703e2018-04-13 12:05:36 +0200123 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);
Arnaud Patardc197da92010-04-29 11:58:54 +0200134}
Huacai Chencbfb3ea2015-04-01 10:20:09 +0800135postcore_initcall(loongson_gpio_setup);