blob: ec1b2d8f61c475db0cbd7b7690b3a8522247e11b [file] [log] [blame]
eric miao2c8086a2007-09-11 19:13:17 -07001/*
2 * linux/arch/arm/mach-pxa/mfp.c
3 *
4 * PXA3xx Multi-Function Pin Support
5 *
6 * Copyright (C) 2007 Marvell Internation Ltd.
7 *
eric miaoe9bba8e2007-10-30 08:01:38 +01008 * 2007-08-21: eric miao <eric.miao@marvell.com>
eric miao2c8086a2007-09-11 19:13:17 -07009 * 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>
Russell Kingd4fc8582008-01-08 15:12:22 +000020#include <linux/sysdev.h>
eric miao2c8086a2007-09-11 19:13:17 -070021
22#include <asm/hardware.h>
23#include <asm/arch/mfp.h>
eric miao7f7c8a62008-01-03 11:25:56 +080024#include <asm/arch/mfp-pxa3xx.h>
eric miao2c8086a2007-09-11 19:13:17 -070025
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 */
30static DEFINE_SPINLOCK(mfp_spin_lock);
31
32static void __iomem *mfpr_mmio_base = (void __iomem *)&__REG(MFPR_BASE);
eric miao7f7c8a62008-01-03 11:25:56 +080033
34struct 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
eric miao2c8086a2007-09-11 19:13:17 -070041static struct pxa3xx_mfp_pin mfp_table[MFP_PIN_MAX];
42
eric miao7f7c8a62008-01-03 11:25:56 +080043/* mapping of MFP_LPM_* definitions to MFPR_LPM_* register bits */
44const 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 */
54const 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 */
62const static unsigned long mfpr_edge[] = {
63 MFPR_EDGE_NONE,
64 MFPR_EDGE_RISE,
65 MFPR_EDGE_FALL,
66 MFPR_EDGE_BOTH,
67};
68
eric miao2c8086a2007-09-11 19:13:17 -070069#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
eric miao7f7c8a62008-01-03 11:25:56 +080075#define mfp_configured(p) ((p)->config != -1)
76
eric miao2c8086a2007-09-11 19:13:17 -070077/*
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
eric miao7f7c8a62008-01-03 11:25:56 +080083static inline void __mfp_config_run(struct pxa3xx_mfp_pin *p)
eric miao2c8086a2007-09-11 19:13:17 -070084{
eric miao7f7c8a62008-01-03 11:25:56 +080085 if (mfp_configured(p))
86 mfpr_writel(p->mfpr_off, p->mfpr_run);
eric miao2c8086a2007-09-11 19:13:17 -070087}
88
eric miao7f7c8a62008-01-03 11:25:56 +080089static inline void __mfp_config_lpm(struct pxa3xx_mfp_pin *p)
eric miao2c8086a2007-09-11 19:13:17 -070090{
Russell Kingd4fc8582008-01-08 15:12:22 +000091 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 }
eric miao7f7c8a62008-01-03 11:25:56 +080098}
99
100void pxa3xx_mfp_config(unsigned long *mfp_cfgs, int num)
101{
102 unsigned long flags;
103 int i;
eric miao2c8086a2007-09-11 19:13:17 -0700104
105 spin_lock_irqsave(&mfp_spin_lock, flags);
106
eric miao7f7c8a62008-01-03 11:25:56 +0800107 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;
eric miao2c8086a2007-09-11 19:13:17 -0700111
eric miao7f7c8a62008-01-03 11:25:56 +0800112 pin = MFP_PIN(c);
eric miao2c8086a2007-09-11 19:13:17 -0700113 BUG_ON(pin >= MFP_PIN_MAX);
eric miao7f7c8a62008-01-03 11:25:56 +0800114 p = &mfp_table[pin];
eric miao2c8086a2007-09-11 19:13:17 -0700115
eric miao7f7c8a62008-01-03 11:25:56 +0800116 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);
eric miao2c8086a2007-09-11 19:13:17 -0700137 }
138
139 mfpr_sync();
140 spin_unlock_irqrestore(&mfp_spin_lock, flags);
141}
142
143unsigned 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
156void 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
eric miao2c8086a2007-09-11 19:13:17 -0700168void __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;
eric miao7f7c8a62008-01-03 11:25:56 +0800182 mfp_table[i].mfpr_run = 0;
183 mfp_table[i].mfpr_lpm = 0;
eric miao2c8086a2007-09-11 19:13:17 -0700184 offset += 4; i++;
185 } while ((i <= p->end) && (p->end != -1));
186 }
187
188 spin_unlock_irqrestore(&mfp_spin_lock, flags);
189}
190
191void __init pxa3xx_init_mfp(void)
192{
eric miao7f7c8a62008-01-03 11:25:56 +0800193 int i;
194
195 for (i = 0; i < ARRAY_SIZE(mfp_table); i++)
196 mfp_table[i].config = -1;
eric miao2c8086a2007-09-11 19:13:17 -0700197}
Russell Kingd4fc8582008-01-08 15:12:22 +0000198
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 */
207static 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
218static 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
229static struct sysdev_class mfp_sysclass = {
230 set_kset_name("mfp"),
231 .suspend = pxa3xx_mfp_suspend,
232 .resume = pxa3xx_mfp_resume,
233};
234
235static struct sys_device mfp_device = {
236 .id = 0,
237 .cls = &mfp_sysclass,
238};
239
240static int __init mfp_init_devicefs(void)
241{
242 sysdev_class_register(&mfp_sysclass);
243 return sysdev_register(&mfp_device);
244}
245device_initcall(mfp_init_devicefs);
246#endif