This commit was manufactured by cvs2svn to create branch 'fedora'.
[linux-2.6.git] / drivers / pcmcia / sa11xx_base.c
1 /*======================================================================
2
3     Device driver for the PCMCIA control functionality of StrongARM
4     SA-1100 microprocessors.
5
6     The contents of this file are subject to the Mozilla Public
7     License Version 1.1 (the "License"); you may not use this file
8     except in compliance with the License. You may obtain a copy of
9     the License at http://www.mozilla.org/MPL/
10
11     Software distributed under the License is distributed on an "AS
12     IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
13     implied. See the License for the specific language governing
14     rights and limitations under the License.
15
16     The initial developer of the original code is John G. Dorsey
17     <john+@cs.cmu.edu>.  Portions created by John G. Dorsey are
18     Copyright (C) 1999 John G. Dorsey.  All Rights Reserved.
19
20     Alternatively, the contents of this file may be used under the
21     terms of the GNU Public License version 2 (the "GPL"), in which
22     case the provisions of the GPL are applicable instead of the
23     above.  If you wish to allow the use of your version of this file
24     only under the terms of the GPL and not to allow others to use
25     your version of this file under the MPL, indicate your decision
26     by deleting the provisions above and replace them with the notice
27     and other provisions required by the GPL.  If you do not delete
28     the provisions above, a recipient may use your version of this
29     file under either the MPL or the GPL.
30
31 ======================================================================*/
32
33 #include <linux/module.h>
34 #include <linux/init.h>
35 #include <linux/config.h>
36 #include <linux/cpufreq.h>
37 #include <linux/ioport.h>
38 #include <linux/kernel.h>
39 #include <linux/notifier.h>
40 #include <linux/spinlock.h>
41
42 #include <asm/hardware.h>
43 #include <asm/io.h>
44 #include <asm/irq.h>
45 #include <asm/system.h>
46
47 #include "soc_common.h"
48 #include "sa11xx_base.h"
49
50
51 /*
52  * sa1100_pcmcia_default_mecr_timing
53  * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
54  *
55  * Calculate MECR clock wait states for given CPU clock
56  * speed and command wait state. This function can be over-
57  * written by a board specific version.
58  *
59  * The default is to simply calculate the BS values as specified in
60  * the INTEL SA1100 development manual
61  * "Expansion Memory (PCMCIA) Configuration Register (MECR)"
62  * that's section 10.2.5 in _my_ version of the manual ;)
63  */
64 static unsigned int
65 sa1100_pcmcia_default_mecr_timing(struct soc_pcmcia_socket *skt,
66                                   unsigned int cpu_speed,
67                                   unsigned int cmd_time)
68 {
69         return sa1100_pcmcia_mecr_bs(cmd_time, cpu_speed);
70 }
71
72 /* sa1100_pcmcia_set_mecr()
73  * ^^^^^^^^^^^^^^^^^^^^^^^^
74  *
75  * set MECR value for socket <sock> based on this sockets
76  * io, mem and attribute space access speed.
77  * Call board specific BS value calculation to allow boards
78  * to tweak the BS values.
79  */
80 static int
81 sa1100_pcmcia_set_mecr(struct soc_pcmcia_socket *skt, unsigned int cpu_clock)
82 {
83         struct soc_pcmcia_timing timing;
84         u32 mecr, old_mecr;
85         unsigned long flags;
86         unsigned int bs_io, bs_mem, bs_attr;
87
88         soc_common_pcmcia_get_timing(skt, &timing);
89
90         bs_io = skt->ops->get_timing(skt, cpu_clock, timing.io);
91         bs_mem = skt->ops->get_timing(skt, cpu_clock, timing.mem);
92         bs_attr = skt->ops->get_timing(skt, cpu_clock, timing.attr);
93
94         local_irq_save(flags);
95
96         old_mecr = mecr = MECR;
97         MECR_FAST_SET(mecr, skt->nr, 0);
98         MECR_BSIO_SET(mecr, skt->nr, bs_io);
99         MECR_BSA_SET(mecr, skt->nr, bs_attr);
100         MECR_BSM_SET(mecr, skt->nr, bs_mem);
101         if (old_mecr != mecr)
102                 MECR = mecr;
103
104         local_irq_restore(flags);
105
106         debug(skt, 2, "FAST %X  BSM %X  BSA %X  BSIO %X\n",
107               MECR_FAST_GET(mecr, skt->nr),
108               MECR_BSM_GET(mecr, skt->nr), MECR_BSA_GET(mecr, skt->nr),
109               MECR_BSIO_GET(mecr, skt->nr));
110
111         return 0;
112 }
113
114 static int
115 sa1100_pcmcia_set_timing(struct soc_pcmcia_socket *skt)
116 {
117         return sa1100_pcmcia_set_mecr(skt, cpufreq_get(0));
118 }
119
120 static int
121 sa1100_pcmcia_show_timing(struct soc_pcmcia_socket *skt, char *buf)
122 {
123         struct soc_pcmcia_timing timing;
124         unsigned int clock = cpufreq_get(0);
125         unsigned long mecr = MECR;
126         char *p = buf;
127
128         soc_common_pcmcia_get_timing(skt, &timing);
129
130         p+=sprintf(p, "I/O      : %u (%u)\n", timing.io,
131                    sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, skt->nr)));
132
133         p+=sprintf(p, "attribute: %u (%u)\n", timing.attr,
134                    sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, skt->nr)));
135
136         p+=sprintf(p, "common   : %u (%u)\n", timing.mem,
137                    sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, skt->nr)));
138
139         return p - buf;
140 }
141
142 int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops,
143                             int first, int nr)
144 {
145         /*
146          * set default MECR calculation if the board specific
147          * code did not specify one...
148          */
149         if (!ops->get_timing)
150                 ops->get_timing = sa1100_pcmcia_default_mecr_timing;
151
152         /* Provide our SA11x0 specific timing routines. */
153         ops->set_timing  = sa1100_pcmcia_set_timing;
154         ops->show_timing = sa1100_pcmcia_show_timing;
155
156         return soc_common_drv_pcmcia_probe(dev, ops, first, nr);
157 }
158 EXPORT_SYMBOL(sa11xx_drv_pcmcia_probe);
159
160 #ifdef CONFIG_CPU_FREQ
161
162 /* sa1100_pcmcia_update_mecr()
163  * ^^^^^^^^^^^^^^^^^^^^^^^^^^^
164  * When sa1100_pcmcia_notifier() decides that a MECR adjustment (due
165  * to a core clock frequency change) is needed, this routine establishes
166  * new BS_xx values consistent with the clock speed `clock'.
167  */
168 static void sa1100_pcmcia_update_mecr(unsigned int clock)
169 {
170         struct soc_pcmcia_socket *skt;
171
172         down(&soc_pcmcia_sockets_lock);
173         list_for_each_entry(skt, &soc_pcmcia_sockets, node)
174                 sa1100_pcmcia_set_mecr(skt, clock);
175         up(&soc_pcmcia_sockets_lock);
176 }
177
178 /* sa1100_pcmcia_notifier()
179  * ^^^^^^^^^^^^^^^^^^^^^^^^
180  * When changing the processor core clock frequency, it is necessary
181  * to adjust the MECR timings accordingly. We've recorded the timings
182  * requested by Card Services, so this is just a matter of finding
183  * out what our current speed is, and then recomputing the new MECR
184  * values.
185  *
186  * Returns: 0 on success, -1 on error
187  */
188 static int
189 sa1100_pcmcia_notifier(struct notifier_block *nb, unsigned long val,
190                        void *data)
191 {
192         struct cpufreq_freqs *freqs = data;
193
194         switch (val) {
195         case CPUFREQ_PRECHANGE:
196                 if (freqs->new > freqs->old)
197                         sa1100_pcmcia_update_mecr(freqs->new);
198                 break;
199
200         case CPUFREQ_POSTCHANGE:
201                 if (freqs->new < freqs->old)
202                         sa1100_pcmcia_update_mecr(freqs->new);
203                 break;
204         case CPUFREQ_RESUMECHANGE:
205                 sa1100_pcmcia_update_mecr(freqs->new);
206                 break;
207         }
208
209         return 0;
210 }
211
212 static struct notifier_block sa1100_pcmcia_notifier_block = {
213         .notifier_call  = sa1100_pcmcia_notifier
214 };
215
216 static int __init sa11xx_pcmcia_init(void)
217 {
218         int ret;
219
220         printk(KERN_INFO "SA11xx PCMCIA\n");
221
222         ret = cpufreq_register_notifier(&sa1100_pcmcia_notifier_block,
223                                         CPUFREQ_TRANSITION_NOTIFIER);
224         if (ret < 0)
225                 printk(KERN_ERR "Unable to register CPU frequency change "
226                         "notifier (%d)\n", ret);
227
228         return ret;
229 }
230 module_init(sa11xx_pcmcia_init);
231
232 static void __exit sa11xx_pcmcia_exit(void)
233 {
234         cpufreq_unregister_notifier(&sa1100_pcmcia_notifier_block, CPUFREQ_TRANSITION_NOTIFIER);
235 }
236
237 module_exit(sa11xx_pcmcia_exit);
238 #endif
239
240 MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
241 MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-11xx core socket driver");
242 MODULE_LICENSE("Dual MPL/GPL");