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