Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
c197da91 | 2 | /* |
cbfb3ea7 | 3 | * Loongson-2F/3A/3B GPIO Support |
c197da91 | 4 | * |
70342287 | 5 | * Copyright (c) 2008 Richard Liu, STMicroelectronics <richard.liu@st.com> |
c197da91 | 6 | * Copyright (c) 2008-2010 Arnaud Patard <apatard@mandriva.com> |
cbfb3ea7 HC |
7 | * Copyright (c) 2013 Hongbing Hu <huhb@lemote.com> |
8 | * Copyright (c) 2014 Huacai Chen <chenhc@lemote.com> | |
c197da91 AP |
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> | |
f105edf7 | 16 | #include <linux/gpio/driver.h> |
70e703e7 | 17 | #include <linux/platform_device.h> |
a4e5db83 | 18 | #include <linux/bitops.h> |
c197da91 AP |
19 | #include <asm/types.h> |
20 | #include <loongson.h> | |
c197da91 AP |
21 | |
22 | #define STLS2F_N_GPIO 4 | |
cbfb3ea7 HC |
23 | #define STLS3A_N_GPIO 16 |
24 | ||
268a2d60 | 25 | #ifdef CONFIG_CPU_LOONGSON64 |
cbfb3ea7 HC |
26 | #define LOONGSON_N_GPIO STLS3A_N_GPIO |
27 | #else | |
28 | #define LOONGSON_N_GPIO STLS2F_N_GPIO | |
29 | #endif | |
30 | ||
a4e5db83 LW |
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 | */ | |
cbfb3ea7 | 36 | #define LOONGSON_GPIO_IN_OFFSET 16 |
c197da91 AP |
37 | |
38 | static DEFINE_SPINLOCK(gpio_lock); | |
39 | ||
cbfb3ea7 | 40 | static int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio) |
c197da91 | 41 | { |
df5dade4 | 42 | u32 val; |
df5dade4 | 43 | |
df5dade4 HC |
44 | spin_lock(&gpio_lock); |
45 | val = LOONGSON_GPIODATA; | |
46 | spin_unlock(&gpio_lock); | |
47 | ||
a4e5db83 | 48 | return !!(val & BIT(gpio + LOONGSON_GPIO_IN_OFFSET)); |
c197da91 AP |
49 | } |
50 | ||
cbfb3ea7 | 51 | static void loongson_gpio_set_value(struct gpio_chip *chip, |
c197da91 AP |
52 | unsigned gpio, int value) |
53 | { | |
df5dade4 | 54 | u32 val; |
df5dade4 HC |
55 | |
56 | spin_lock(&gpio_lock); | |
57 | val = LOONGSON_GPIODATA; | |
58 | if (value) | |
a4e5db83 | 59 | val |= BIT(gpio); |
df5dade4 | 60 | else |
a4e5db83 | 61 | val &= ~BIT(gpio); |
df5dade4 HC |
62 | LOONGSON_GPIODATA = val; |
63 | spin_unlock(&gpio_lock); | |
c197da91 AP |
64 | } |
65 | ||
f105edf7 LW |
66 | static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) |
67 | { | |
68 | u32 temp; | |
f105edf7 LW |
69 | |
70 | spin_lock(&gpio_lock); | |
f105edf7 | 71 | temp = LOONGSON_GPIOIE; |
a4e5db83 | 72 | temp |= BIT(gpio); |
f105edf7 LW |
73 | LOONGSON_GPIOIE = temp; |
74 | spin_unlock(&gpio_lock); | |
75 | ||
76 | return 0; | |
77 | } | |
78 | ||
79 | static int loongson_gpio_direction_output(struct gpio_chip *chip, | |
80 | unsigned gpio, int level) | |
81 | { | |
82 | u32 temp; | |
f105edf7 LW |
83 | |
84 | loongson_gpio_set_value(chip, gpio, level); | |
85 | spin_lock(&gpio_lock); | |
f105edf7 | 86 | temp = LOONGSON_GPIOIE; |
a4e5db83 | 87 | temp &= ~BIT(gpio); |
f105edf7 LW |
88 | LOONGSON_GPIOIE = temp; |
89 | spin_unlock(&gpio_lock); | |
90 | ||
91 | return 0; | |
92 | } | |
93 | ||
70e703e7 LW |
94 | static 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 | ||
114 | static struct platform_driver loongson_gpio_driver = { | |
115 | .driver = { | |
116 | .name = "loongson-gpio", | |
117 | }, | |
118 | .probe = loongson_gpio_probe, | |
c197da91 AP |
119 | }; |
120 | ||
cbfb3ea7 | 121 | static int __init loongson_gpio_setup(void) |
c197da91 | 122 | { |
70e703e7 LW |
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); | |
c197da91 | 134 | } |
cbfb3ea7 | 135 | postcore_initcall(loongson_gpio_setup); |