Merge tag 'staging-3.12-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh...
[platform/adaptation/renesas_rcar/renesas_kernel.git] / drivers / gpio / gpio-it8761e.c
1 /*
2  *  GPIO interface for IT8761E Super I/O chip
3  *
4  *  Author: Denis Turischev <denis@compulab.co.il>
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 2 as published
8  *  by the Free Software Foundation.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 #include <linux/init.h>
21 #include <linux/kernel.h>
22 #include <linux/module.h>
23 #include <linux/io.h>
24 #include <linux/errno.h>
25 #include <linux/ioport.h>
26
27 #include <linux/gpio.h>
28
29 #define SIO_CHIP_ID             0x8761
30 #define CHIP_ID_HIGH_BYTE       0x20
31 #define CHIP_ID_LOW_BYTE        0x21
32
33 static u8 ports[2] = { 0x2e, 0x4e };
34 static u8 port;
35
36 static DEFINE_SPINLOCK(sio_lock);
37
38 #define GPIO_NAME               "it8761-gpio"
39 #define GPIO_BA_HIGH_BYTE       0x60
40 #define GPIO_BA_LOW_BYTE        0x61
41 #define GPIO_IOSIZE             4
42 #define GPIO1X_IO               0xf0
43 #define GPIO2X_IO               0xf1
44
45 static u16 gpio_ba;
46
47 static u8 read_reg(u8 addr, u8 port)
48 {
49         outb(addr, port);
50         return inb(port + 1);
51 }
52
53 static void write_reg(u8 data, u8 addr, u8 port)
54 {
55         outb(addr, port);
56         outb(data, port + 1);
57 }
58
59 static void enter_conf_mode(u8 port)
60 {
61         outb(0x87, port);
62         outb(0x61, port);
63         outb(0x55, port);
64         outb((port == 0x2e) ? 0x55 : 0xaa, port);
65 }
66
67 static void exit_conf_mode(u8 port)
68 {
69         outb(0x2, port);
70         outb(0x2, port + 1);
71 }
72
73 static void enter_gpio_mode(u8 port)
74 {
75         write_reg(0x2, 0x7, port);
76 }
77
78 static int it8761e_gpio_get(struct gpio_chip *gc, unsigned gpio_num)
79 {
80         u16 reg;
81         u8 bit;
82
83         bit = gpio_num % 8;
84         reg = (gpio_num >= 8) ? gpio_ba + 1 : gpio_ba;
85
86         return !!(inb(reg) & (1 << bit));
87 }
88
89 static int it8761e_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num)
90 {
91         u8 curr_dirs;
92         u8 io_reg, bit;
93
94         bit = gpio_num % 8;
95         io_reg = (gpio_num >= 8) ? GPIO2X_IO : GPIO1X_IO;
96
97         spin_lock(&sio_lock);
98
99         enter_conf_mode(port);
100         enter_gpio_mode(port);
101
102         curr_dirs = read_reg(io_reg, port);
103
104         if (curr_dirs & (1 << bit))
105                 write_reg(curr_dirs & ~(1 << bit), io_reg, port);
106
107         exit_conf_mode(port);
108
109         spin_unlock(&sio_lock);
110         return 0;
111 }
112
113 static void it8761e_gpio_set(struct gpio_chip *gc,
114                                 unsigned gpio_num, int val)
115 {
116         u8 curr_vals, bit;
117         u16 reg;
118
119         bit = gpio_num % 8;
120         reg = (gpio_num >= 8) ? gpio_ba + 1 : gpio_ba;
121
122         spin_lock(&sio_lock);
123
124         curr_vals = inb(reg);
125         if (val)
126                 outb(curr_vals | (1 << bit) , reg);
127         else
128                 outb(curr_vals & ~(1 << bit), reg);
129
130         spin_unlock(&sio_lock);
131 }
132
133 static int it8761e_gpio_direction_out(struct gpio_chip *gc,
134                                         unsigned gpio_num, int val)
135 {
136         u8 curr_dirs, io_reg, bit;
137
138         bit = gpio_num % 8;
139         io_reg = (gpio_num >= 8) ? GPIO2X_IO : GPIO1X_IO;
140
141         it8761e_gpio_set(gc, gpio_num, val);
142
143         spin_lock(&sio_lock);
144
145         enter_conf_mode(port);
146         enter_gpio_mode(port);
147
148         curr_dirs = read_reg(io_reg, port);
149
150         if (!(curr_dirs & (1 << bit)))
151                 write_reg(curr_dirs | (1 << bit), io_reg, port);
152
153         exit_conf_mode(port);
154
155         spin_unlock(&sio_lock);
156         return 0;
157 }
158
159 static struct gpio_chip it8761e_gpio_chip = {
160         .label                  = GPIO_NAME,
161         .owner                  = THIS_MODULE,
162         .get                    = it8761e_gpio_get,
163         .direction_input        = it8761e_gpio_direction_in,
164         .set                    = it8761e_gpio_set,
165         .direction_output       = it8761e_gpio_direction_out,
166 };
167
168 static int __init it8761e_gpio_init(void)
169 {
170         int i, id, err;
171
172         /* chip and port detection */
173         for (i = 0; i < ARRAY_SIZE(ports); i++) {
174                 spin_lock(&sio_lock);
175                 enter_conf_mode(ports[i]);
176
177                 id = (read_reg(CHIP_ID_HIGH_BYTE, ports[i]) << 8) +
178                                 read_reg(CHIP_ID_LOW_BYTE, ports[i]);
179
180                 exit_conf_mode(ports[i]);
181                 spin_unlock(&sio_lock);
182
183                 if (id == SIO_CHIP_ID) {
184                         port = ports[i];
185                         break;
186                 }
187         }
188
189         if (!port)
190                 return -ENODEV;
191
192         /* fetch GPIO base address */
193         enter_conf_mode(port);
194         enter_gpio_mode(port);
195         gpio_ba = (read_reg(GPIO_BA_HIGH_BYTE, port) << 8) +
196                                 read_reg(GPIO_BA_LOW_BYTE, port);
197         exit_conf_mode(port);
198
199         if (!request_region(gpio_ba, GPIO_IOSIZE, GPIO_NAME))
200                 return -EBUSY;
201
202         it8761e_gpio_chip.base = -1;
203         it8761e_gpio_chip.ngpio = 16;
204
205         err = gpiochip_add(&it8761e_gpio_chip);
206         if (err < 0)
207                 goto gpiochip_add_err;
208
209         return 0;
210
211 gpiochip_add_err:
212         release_region(gpio_ba, GPIO_IOSIZE);
213         gpio_ba = 0;
214         return err;
215 }
216
217 static void __exit it8761e_gpio_exit(void)
218 {
219         if (gpio_ba) {
220                 int ret = gpiochip_remove(&it8761e_gpio_chip);
221
222                 WARN(ret, "%s(): gpiochip_remove() failed, ret=%d\n",
223                                 __func__, ret);
224
225                 release_region(gpio_ba, GPIO_IOSIZE);
226                 gpio_ba = 0;
227         }
228 }
229 module_init(it8761e_gpio_init);
230 module_exit(it8761e_gpio_exit);
231
232 MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
233 MODULE_DESCRIPTION("GPIO interface for IT8761E Super I/O chip");
234 MODULE_LICENSE("GPL");