Root/
1 | /* |
2 | * it8761_gpio.c - 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 | gpiochip_remove(&it8761e_gpio_chip); |
221 | |
222 | release_region(gpio_ba, GPIO_IOSIZE); |
223 | gpio_ba = 0; |
224 | } |
225 | } |
226 | module_init(it8761e_gpio_init); |
227 | module_exit(it8761e_gpio_exit); |
228 | |
229 | MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); |
230 | MODULE_DESCRIPTION("GPIO interface for IT8761E Super I/O chip"); |
231 | MODULE_LICENSE("GPL"); |
232 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9