Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-2.6
[linux-2.6] / arch / arm / mach-pxa / mfp.c
1 /*
2  * linux/arch/arm/mach-pxa/mfp.c
3  *
4  * PXA3xx Multi-Function Pin Support
5  *
6  * Copyright (C) 2007 Marvell Internation Ltd.
7  *
8  * 2007-08-21: eric miao <eric.miao@marvell.com>
9  *             initial version
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License version 2 as
13  *  published by the Free Software Foundation.
14  */
15
16 #include <linux/module.h>
17 #include <linux/kernel.h>
18 #include <linux/init.h>
19 #include <linux/io.h>
20 #include <linux/sysdev.h>
21
22 #include <asm/hardware.h>
23 #include <asm/arch/mfp.h>
24 #include <asm/arch/mfp-pxa3xx.h>
25
26 /* mfp_spin_lock is used to ensure that MFP register configuration
27  * (most likely a read-modify-write operation) is atomic, and that
28  * mfp_table[] is consistent
29  */
30 static DEFINE_SPINLOCK(mfp_spin_lock);
31
32 static void __iomem *mfpr_mmio_base = (void __iomem *)&__REG(MFPR_BASE);
33
34 struct pxa3xx_mfp_pin {
35         unsigned long   config;         /* -1 for not configured */
36         unsigned long   mfpr_off;       /* MFPRxx Register offset */
37         unsigned long   mfpr_run;       /* Run-Mode Register Value */
38         unsigned long   mfpr_lpm;       /* Low Power Mode Register Value */
39 };
40
41 static struct pxa3xx_mfp_pin mfp_table[MFP_PIN_MAX];
42
43 /* mapping of MFP_LPM_* definitions to MFPR_LPM_* register bits */
44 const static unsigned long mfpr_lpm[] = {
45         MFPR_LPM_INPUT,
46         MFPR_LPM_DRIVE_LOW,
47         MFPR_LPM_DRIVE_HIGH,
48         MFPR_LPM_PULL_LOW,
49         MFPR_LPM_PULL_HIGH,
50         MFPR_LPM_FLOAT,
51 };
52
53 /* mapping of MFP_PULL_* definitions to MFPR_PULL_* register bits */
54 const static unsigned long mfpr_pull[] = {
55         MFPR_PULL_NONE,
56         MFPR_PULL_LOW,
57         MFPR_PULL_HIGH,
58         MFPR_PULL_BOTH,
59 };
60
61 /* mapping of MFP_LPM_EDGE_* definitions to MFPR_EDGE_* register bits */
62 const static unsigned long mfpr_edge[] = {
63         MFPR_EDGE_NONE,
64         MFPR_EDGE_RISE,
65         MFPR_EDGE_FALL,
66         MFPR_EDGE_BOTH,
67 };
68
69 #define mfpr_readl(off)                 \
70         __raw_readl(mfpr_mmio_base + (off))
71
72 #define mfpr_writel(off, val)           \
73         __raw_writel(val, mfpr_mmio_base + (off))
74
75 #define mfp_configured(p)       ((p)->config != -1)
76
77 /*
78  * perform a read-back of any MFPR register to make sure the
79  * previous writings are finished
80  */
81 #define mfpr_sync()     (void)__raw_readl(mfpr_mmio_base + 0)
82
83 static inline void __mfp_config_run(struct pxa3xx_mfp_pin *p)
84 {
85         if (mfp_configured(p))
86                 mfpr_writel(p->mfpr_off, p->mfpr_run);
87 }
88
89 static inline void __mfp_config_lpm(struct pxa3xx_mfp_pin *p)
90 {
91         if (mfp_configured(p)) {
92                 unsigned long mfpr_clr = (p->mfpr_run & ~MFPR_EDGE_BOTH) | MFPR_EDGE_CLEAR;
93                 if (mfpr_clr != p->mfpr_run)
94                         mfpr_writel(p->mfpr_off, mfpr_clr);
95                 if (p->mfpr_lpm != mfpr_clr)
96                         mfpr_writel(p->mfpr_off, p->mfpr_lpm);
97         }
98 }
99
100 void pxa3xx_mfp_config(unsigned long *mfp_cfgs, int num)
101 {
102         unsigned long flags;
103         int i;
104
105         spin_lock_irqsave(&mfp_spin_lock, flags);
106
107         for (i = 0; i < num; i++, mfp_cfgs++) {
108                 unsigned long tmp, c = *mfp_cfgs;
109                 struct pxa3xx_mfp_pin *p;
110                 int pin, af, drv, lpm, edge, pull;
111
112                 pin = MFP_PIN(c);
113                 BUG_ON(pin >= MFP_PIN_MAX);
114                 p = &mfp_table[pin];
115
116                 af  = MFP_AF(c);
117                 drv = MFP_DS(c);
118                 lpm = MFP_LPM_STATE(c);
119                 edge = MFP_LPM_EDGE(c);
120                 pull = MFP_PULL(c);
121
122                 /* run-mode pull settings will conflict with MFPR bits of
123                  * low power mode state,  calculate mfpr_run and mfpr_lpm
124                  * individually if pull != MFP_PULL_NONE
125                  */
126                 tmp = MFPR_AF_SEL(af) | MFPR_DRIVE(drv);
127
128                 if (likely(pull == MFP_PULL_NONE)) {
129                         p->mfpr_run = tmp | mfpr_lpm[lpm] | mfpr_edge[edge];
130                         p->mfpr_lpm = p->mfpr_run;
131                 } else {
132                         p->mfpr_lpm = tmp | mfpr_lpm[lpm] | mfpr_edge[edge];
133                         p->mfpr_run = tmp | mfpr_pull[pull];
134                 }
135
136                 p->config = c; __mfp_config_run(p);
137         }
138
139         mfpr_sync();
140         spin_unlock_irqrestore(&mfp_spin_lock, flags);
141 }
142
143 unsigned long pxa3xx_mfp_read(int mfp)
144 {
145         unsigned long val, flags;
146
147         BUG_ON(mfp >= MFP_PIN_MAX);
148
149         spin_lock_irqsave(&mfp_spin_lock, flags);
150         val = mfpr_readl(mfp_table[mfp].mfpr_off);
151         spin_unlock_irqrestore(&mfp_spin_lock, flags);
152
153         return val;
154 }
155
156 void pxa3xx_mfp_write(int mfp, unsigned long val)
157 {
158         unsigned long flags;
159
160         BUG_ON(mfp >= MFP_PIN_MAX);
161
162         spin_lock_irqsave(&mfp_spin_lock, flags);
163         mfpr_writel(mfp_table[mfp].mfpr_off, val);
164         mfpr_sync();
165         spin_unlock_irqrestore(&mfp_spin_lock, flags);
166 }
167
168 void __init pxa3xx_mfp_init_addr(struct pxa3xx_mfp_addr_map *map)
169 {
170         struct pxa3xx_mfp_addr_map *p;
171         unsigned long offset, flags;
172         int i;
173
174         spin_lock_irqsave(&mfp_spin_lock, flags);
175
176         for (p = map; p->start != MFP_PIN_INVALID; p++) {
177                 offset = p->offset;
178                 i = p->start;
179
180                 do {
181                         mfp_table[i].mfpr_off = offset;
182                         mfp_table[i].mfpr_run = 0;
183                         mfp_table[i].mfpr_lpm = 0;
184                         offset += 4; i++;
185                 } while ((i <= p->end) && (p->end != -1));
186         }
187
188         spin_unlock_irqrestore(&mfp_spin_lock, flags);
189 }
190
191 void __init pxa3xx_init_mfp(void)
192 {
193         int i;
194
195         for (i = 0; i < ARRAY_SIZE(mfp_table); i++)
196                 mfp_table[i].config = -1;
197 }
198
199 #ifdef CONFIG_PM
200 /*
201  * Configure the MFPs appropriately for suspend/resume.
202  * FIXME: this should probably depend on which system state we're
203  * entering - for instance, we might not want to place MFP pins in
204  * a pull-down mode if they're an active low chip select, and we're
205  * just entering standby.
206  */
207 static int pxa3xx_mfp_suspend(struct sys_device *d, pm_message_t state)
208 {
209         int pin;
210
211         for (pin = 0; pin < ARRAY_SIZE(mfp_table); pin++) {
212                 struct pxa3xx_mfp_pin *p = &mfp_table[pin];
213                 __mfp_config_lpm(p);
214         }
215         return 0;
216 }
217
218 static int pxa3xx_mfp_resume(struct sys_device *d)
219 {
220         int pin;
221
222         for (pin = 0; pin < ARRAY_SIZE(mfp_table); pin++) {
223                 struct pxa3xx_mfp_pin *p = &mfp_table[pin];
224                 __mfp_config_run(p);
225         }
226         return 0;
227 }
228
229 static struct sysdev_class mfp_sysclass = {
230         set_kset_name("mfp"),
231         .suspend        = pxa3xx_mfp_suspend,
232         .resume         = pxa3xx_mfp_resume,
233 };
234
235 static struct sys_device mfp_device = {
236         .id             = 0,
237         .cls            = &mfp_sysclass,
238 };
239
240 static int __init mfp_init_devicefs(void)
241 {
242         sysdev_class_register(&mfp_sysclass);
243         return sysdev_register(&mfp_device);
244 }
245 device_initcall(mfp_init_devicefs);
246 #endif