import of ftp.dlink.com/GPL/DSMG-600_reB/ppclinux.tar.gz
[linux-2.4.21-pre4.git] / drivers / char / ibm_ocp_gpio.c
1 /*
2  * FILE NAME ibm_ocp_gpio.c
3  *
4  * BRIEF MODULE DESCRIPTION
5  *  API for IBM PowerPC 4xx GPIO device.
6  *  Driver for IBM PowerPC 4xx GPIO device.
7  *
8  *  Armin Kuster akuster@pacbell.net
9  *  Sept, 2001
10  *
11  *  Orignial driver
12  *  Author: MontaVista Software, Inc.  <source@mvista.com>
13  *          Frank Rowand <frank_rowand@mvista.com>
14  *          Debbie Chu   <debbie_chu@mvista.com>
15  *
16  * Copyright 2000,2001,2002 MontaVista Software Inc.
17  *
18  *  This program is free software; you can redistribute  it and/or modify it
19  *  under  the terms of  the GNU General  Public License as published by the
20  *  Free Software Foundation;  either version 2 of the  License, or (at your
21  *  option) any later version.
22  *
23  *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
24  *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
25  *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
26  *  NO  EVENT  SHALL   THE AUTHOR  BE   LIABLE FOR ANY   DIRECT, INDIRECT,
27  *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28  *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
29  *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
30  *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
31  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32  *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  *
34  *  You should have received a copy of the  GNU General Public License along
35  *  with this program; if not, write  to the Free Software Foundation, Inc.,
36  *  675 Mass Ave, Cambridge, MA 02139, USA.
37  *
38  *      TODO: devfs
39  *
40  *      Version: 02/01/12 - Armin
41  *                       converted to ocp and using ioremap
42  *
43  *      1.2 02/21/01 - Armin
44  *              minor compiler warning fixes
45  *
46  *      1.3 02/22/01 - Armin
47  *              added apm
48  *
49  *      1.4 05/07/02 - Armin/David Mueller
50  *              coverted to core_ocp[];
51  *
52  *      1.5 05/25/02 - Armin
53  *       name change from *_driver to *_dev
54  *
55  *      1.6 06/04/02 - Matt Porter
56  *      ioremap paddr. Comment as 4xx generic driver.
57  *      Fix header to be userland safe and locate in
58  *      an accessible area.  Add ioctl to configure
59  *      multiplexed GPIO pins.
60  *
61  *      1.7 07/25/02 - Armin
62  *      added CPM to enable/disable in init/exit
63  *
64  */
65
66 #define VUFX "07.25.02"
67
68 #include <linux/module.h>
69 #include <linux/config.h>
70 #include <linux/types.h>
71 #include <linux/kernel.h>
72 #include <linux/miscdevice.h>
73 #include <linux/init.h>
74 #include <linux/ioctl.h>
75 #include <linux/pm.h>
76 #include <linux/ibm_ocp_gpio.h>
77 #include <asm/uaccess.h>
78 #include <asm/io.h>
79 #include <asm/machdep.h>
80 #include <asm/ocp.h>
81
82 struct miscdevice ibm_gpio_miscdev;
83 static struct gpio_regs *gpiop;
84
85 #ifdef CONFIG_PM
86 static struct pm_dev *pm_gpio;
87
88 static int
89 gpio_save_state(u32 state)
90 {
91         return 0;
92 }
93
94 static int
95 gpio_suspend(u32 state)
96 {
97         mtdcr(DCRN_CPMFR, mfdcr(DCRN_CPMFR) | state);
98         return 0;
99 }
100
101 static int
102 gpio_resume(u32 state)
103 {
104         mtdcr(DCRN_CPMFR, mfdcr(DCRN_CPMFR) & ~state);
105         return 0;
106 }
107 #endif
108
109 int
110 ibm_gpio_config(__u32 device, __u32 mask, __u32 data)
111 {
112         u32 cfg_reg;
113
114         if (device != 0)
115                 return -ENXIO;
116 #ifdef CONFIG_PM
117         pm_access(pm_gpio);
118 #endif
119
120 #ifdef CONFIG_40x
121         /*
122          * PPC405 uses CPC0_CR0 to select multiplexed GPIO pins.
123          */
124         cfg_reg = mfdcr(DCRN_CHCR0);
125         cfg_reg = (cfg_reg & ~mask) | (data & mask);
126         mtdcr(DCRN_CHCR0, cfg_reg);
127 #elif CONFIG_440
128         /*
129          * PPC440 uses CPC0_GPIO to select multiplexed GPIO pins.
130          */
131         cfg_reg = mfdcr(DCRN_CPC0_GPIO);
132         cfg_reg = (cfg_reg & ~mask) | (data & mask);
133         mtdcr(DCRN_CPC0_GPIO, cfg_reg);
134 #else
135 #error This driver is only supported on PPC40x and PPC440 CPUs
136 #endif
137
138         return 0;
139 }
140
141 int
142 ibm_gpio_tristate(__u32 device, __u32 mask, __u32 data)
143 {
144         if (device != 0)
145                 return -ENXIO;
146 #ifdef CONFIG_PM
147         pm_access(pm_gpio);
148 #endif
149         gpiop->tcr = (gpiop->tcr & ~mask) | (data & mask);
150         return 0;
151 }
152
153 int
154 ibm_gpio_open_drain(__u32 device, __u32 mask, __u32 data)
155 {
156         if (device != 0)
157                 return -ENXIO;
158 #ifdef CONFIG_PM
159         pm_access(pm_gpio);
160 #endif
161         gpiop->odr = (gpiop->odr & ~mask) | (data & mask);
162
163         return 0;
164 }
165
166 int
167 ibm_gpio_in(__u32 device, __u32 mask, volatile __u32 * data)
168 {
169         if (device != 0)
170                 return -ENXIO;
171 #ifdef CONFIG_PM
172         pm_access(pm_gpio);
173 #endif
174         gpiop->tcr = gpiop->tcr & ~mask;
175         eieio();
176
177         /*
178            ** If the previous state was OUT, and gpiop->ir is read once, then the
179            ** data that was being OUTput will be read.  One way to get the right
180            ** data is to read gpiop->ir twice.
181          */
182
183         *data = gpiop->ir;
184         *data = gpiop->ir & mask;
185         eieio();
186         return 0;
187 }
188
189 int
190 ibm_gpio_out(__u32 device, __u32 mask, __u32 data)
191 {
192         if (device != 0)
193                 return -ENXIO;
194 #ifdef CONFIG_PM
195         pm_access(pm_gpio);
196 #endif
197         gpiop->or = (gpiop->or & ~mask) | (data & mask);
198         eieio();
199         gpiop->tcr = gpiop->tcr | mask;
200         eieio();
201         return 0;
202 }
203
204 static int
205 ibm_gpio_open(struct inode *inode, struct file *file)
206 {
207         MOD_INC_USE_COUNT;
208
209         return 0;
210 }
211
212 static int
213 ibm_gpio_release(struct inode *inode, struct file *file)
214 {
215         MOD_DEC_USE_COUNT;
216
217         return 0;
218 }
219
220 static int
221 ibm_gpio_ioctl(struct inode *inode, struct file *file,
222                unsigned int cmd, unsigned long arg)
223 {
224         static struct ibm_gpio_ioctl_data ioctl_data;
225         int status;
226
227         switch (cmd) {
228         case IBMGPIO_IN:
229                 if (copy_from_user(&ioctl_data,
230                                    (struct ibm_gpio_ioctl_data *) arg,
231                                    sizeof (ioctl_data))) {
232                         return -EFAULT;
233                 }
234
235                 status = ibm_gpio_in(ioctl_data.device,
236                                      ioctl_data.mask, &ioctl_data.data);
237                 if (status != 0)
238                         return status;
239
240                 if (copy_to_user((struct ibm_gpio_ioctl_data *) arg,
241                                  &ioctl_data, sizeof (ioctl_data))) {
242                         return -EFAULT;
243                 }
244
245                 break;
246
247         case IBMGPIO_OUT:
248                 if (copy_from_user(&ioctl_data,
249                                    (struct ibm_gpio_ioctl_data *) arg,
250                                    sizeof (ioctl_data))) {
251                         return -EFAULT;
252                 }
253
254                 return ibm_gpio_out(ioctl_data.device,
255                                     ioctl_data.mask, ioctl_data.data);
256
257                 break;
258
259         case IBMGPIO_OPEN_DRAIN:
260                 if (copy_from_user(&ioctl_data,
261                                    (struct ibm_gpio_ioctl_data *) arg,
262                                    sizeof (ioctl_data))) {
263                         return -EFAULT;
264                 }
265
266                 return ibm_gpio_open_drain(ioctl_data.device,
267                                            ioctl_data.mask, ioctl_data.data);
268
269                 break;
270
271         case IBMGPIO_TRISTATE:
272                 if (copy_from_user(&ioctl_data,
273                                    (struct ibm_gpio_ioctl_data *) arg,
274                                    sizeof (ioctl_data)))
275                         return -EFAULT;
276
277                 return ibm_gpio_tristate(ioctl_data.device,
278                                          ioctl_data.mask, ioctl_data.data);
279
280                 break;
281
282         case IBMGPIO_CFG:
283                 if (copy_from_user(&ioctl_data,
284                                    (struct ibm_gpio_ioctl_data *) arg,
285                                    sizeof (ioctl_data)))
286                         return -EFAULT;
287
288                 return ibm_gpio_config(ioctl_data.device,
289                                 ioctl_data.mask, ioctl_data.data);
290
291                 break;
292
293         default:
294                 return -ENOIOCTLCMD;
295
296         }
297         return 0;
298 }
299
300 static struct file_operations ibm_gpio_fops = {
301         owner:THIS_MODULE,
302         ioctl:ibm_gpio_ioctl,
303         open:ibm_gpio_open,
304         release:ibm_gpio_release,
305 };
306
307 static int __init
308 ibm_gpio_init(void)
309 {
310         int curr_gpio = 0;
311         struct ocp_dev *gpio_dev;
312
313         printk("IBM gpio driver version %s\n", VUFX);
314         while (curr_gpio != -ENXIO) {
315                 if (!(gpio_dev = ocp_alloc_dev(0)))
316                         return -ENOMEM;
317
318                 gpio_dev->type = GPIO;
319                 if ((curr_gpio = ocp_register(gpio_dev)) == -ENXIO) {
320                         ocp_free_dev(gpio_dev);
321                         break;
322                 } else {
323                         ibm_gpio_miscdev.minor = 185;   /*GPIO_MINOR; */
324                         ibm_gpio_miscdev.name = gpio_dev->name;
325                         ibm_gpio_miscdev.fops = &ibm_gpio_fops;
326                         misc_register(&ibm_gpio_miscdev);       /*ibm_gpio_miscdev); */
327
328                         gpiop = (struct gpio_regs *) ioremap(gpio_dev->paddr,
329                                         sizeof (struct gpio_regs));
330                         mtdcr(DCRN_CPMFR, mfdcr(DCRN_CPMFR)
331                                         & ~ocp_get_pm(GPIO, curr_gpio));
332                         printk("GPIO #%d at 0x%lx\n", curr_gpio,
333                                (unsigned long) gpiop);
334
335                 }
336         }
337
338         return (curr_gpio == -ENXIO) ? 0 : curr_gpio;
339 }
340
341 static void __exit
342 ibm_gpio_exit(void)
343 {
344         int i;
345         struct ocp_dev *gpio_dev;
346
347         for (i = 0; i < ocp_get_max(GPIO); i++) {
348                 gpio_dev = ocp_get_dev(GPIO, i);
349                 misc_deregister(&ibm_gpio_miscdev);
350                 mtdcr(DCRN_CPMFR, mfdcr(DCRN_CPMFR) | ocp_get_pm(GPIO, i));
351                 ocp_unregister(gpio_dev);
352         }
353 }
354
355 module_init(ibm_gpio_init);
356 module_exit(ibm_gpio_exit);
357
358 EXPORT_SYMBOL(ibm_gpio_tristate);
359 EXPORT_SYMBOL(ibm_gpio_open_drain);
360 EXPORT_SYMBOL(ibm_gpio_in);
361 EXPORT_SYMBOL(ibm_gpio_out);
362
363 MODULE_LICENSE("GPL");