Root/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/cpufreq.h>
36#include <linux/ioport.h>
37#include <linux/kernel.h>
38#include <linux/spinlock.h>
39#include <linux/io.h>
40#include <linux/slab.h>
41
42#include <mach/hardware.h>
43#include <asm/irq.h>
44
45#include "soc_common.h"
46#include "sa11xx_base.h"
47
48
49/*
50 * sa1100_pcmcia_default_mecr_timing
51 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
52 *
53 * Calculate MECR clock wait states for given CPU clock
54 * speed and command wait state. This function can be over-
55 * written by a board specific version.
56 *
57 * The default is to simply calculate the BS values as specified in
58 * the INTEL SA1100 development manual
59 * "Expansion Memory (PCMCIA) Configuration Register (MECR)"
60 * that's section 10.2.5 in _my_ version of the manual ;)
61 */
62static unsigned int
63sa1100_pcmcia_default_mecr_timing(struct soc_pcmcia_socket *skt,
64                  unsigned int cpu_speed,
65                  unsigned int cmd_time)
66{
67    return sa1100_pcmcia_mecr_bs(cmd_time, cpu_speed);
68}
69
70/* sa1100_pcmcia_set_mecr()
71 * ^^^^^^^^^^^^^^^^^^^^^^^^
72 *
73 * set MECR value for socket <sock> based on this sockets
74 * io, mem and attribute space access speed.
75 * Call board specific BS value calculation to allow boards
76 * to tweak the BS values.
77 */
78static int
79sa1100_pcmcia_set_mecr(struct soc_pcmcia_socket *skt, unsigned int cpu_clock)
80{
81    struct soc_pcmcia_timing timing;
82    u32 mecr, old_mecr;
83    unsigned long flags;
84    unsigned int bs_io, bs_mem, bs_attr;
85
86    soc_common_pcmcia_get_timing(skt, &timing);
87
88    bs_io = skt->ops->get_timing(skt, cpu_clock, timing.io);
89    bs_mem = skt->ops->get_timing(skt, cpu_clock, timing.mem);
90    bs_attr = skt->ops->get_timing(skt, cpu_clock, timing.attr);
91
92    local_irq_save(flags);
93
94    old_mecr = mecr = MECR;
95    MECR_FAST_SET(mecr, skt->nr, 0);
96    MECR_BSIO_SET(mecr, skt->nr, bs_io);
97    MECR_BSA_SET(mecr, skt->nr, bs_attr);
98    MECR_BSM_SET(mecr, skt->nr, bs_mem);
99    if (old_mecr != mecr)
100        MECR = mecr;
101
102    local_irq_restore(flags);
103
104    debug(skt, 2, "FAST %X BSM %X BSA %X BSIO %X\n",
105          MECR_FAST_GET(mecr, skt->nr),
106          MECR_BSM_GET(mecr, skt->nr), MECR_BSA_GET(mecr, skt->nr),
107          MECR_BSIO_GET(mecr, skt->nr));
108
109    return 0;
110}
111
112#ifdef CONFIG_CPU_FREQ
113static int
114sa1100_pcmcia_frequency_change(struct soc_pcmcia_socket *skt,
115                   unsigned long val,
116                   struct cpufreq_freqs *freqs)
117{
118    switch (val) {
119    case CPUFREQ_PRECHANGE:
120        if (freqs->new > freqs->old)
121            sa1100_pcmcia_set_mecr(skt, freqs->new);
122        break;
123
124    case CPUFREQ_POSTCHANGE:
125        if (freqs->new < freqs->old)
126            sa1100_pcmcia_set_mecr(skt, freqs->new);
127        break;
128    case CPUFREQ_RESUMECHANGE:
129        sa1100_pcmcia_set_mecr(skt, freqs->new);
130        break;
131    }
132
133    return 0;
134}
135
136#endif
137
138static int
139sa1100_pcmcia_set_timing(struct soc_pcmcia_socket *skt)
140{
141    return sa1100_pcmcia_set_mecr(skt, cpufreq_get(0));
142}
143
144static int
145sa1100_pcmcia_show_timing(struct soc_pcmcia_socket *skt, char *buf)
146{
147    struct soc_pcmcia_timing timing;
148    unsigned int clock = cpufreq_get(0);
149    unsigned long mecr = MECR;
150    char *p = buf;
151
152    soc_common_pcmcia_get_timing(skt, &timing);
153
154    p+=sprintf(p, "I/O : %u (%u)\n", timing.io,
155           sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, skt->nr)));
156
157    p+=sprintf(p, "attribute: %u (%u)\n", timing.attr,
158           sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, skt->nr)));
159
160    p+=sprintf(p, "common : %u (%u)\n", timing.mem,
161           sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, skt->nr)));
162
163    return p - buf;
164}
165
166static const char *skt_names[] = {
167    "PCMCIA socket 0",
168    "PCMCIA socket 1",
169};
170
171#define SKT_DEV_INFO_SIZE(n) \
172    (sizeof(struct skt_dev_info) + (n)*sizeof(struct soc_pcmcia_socket))
173
174int sa11xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt)
175{
176    skt->res_skt.start = _PCMCIA(skt->nr);
177    skt->res_skt.end = _PCMCIA(skt->nr) + PCMCIASp - 1;
178    skt->res_skt.name = skt_names[skt->nr];
179    skt->res_skt.flags = IORESOURCE_MEM;
180
181    skt->res_io.start = _PCMCIAIO(skt->nr);
182    skt->res_io.end = _PCMCIAIO(skt->nr) + PCMCIAIOSp - 1;
183    skt->res_io.name = "io";
184    skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
185
186    skt->res_mem.start = _PCMCIAMem(skt->nr);
187    skt->res_mem.end = _PCMCIAMem(skt->nr) + PCMCIAMemSp - 1;
188    skt->res_mem.name = "memory";
189    skt->res_mem.flags = IORESOURCE_MEM;
190
191    skt->res_attr.start = _PCMCIAAttr(skt->nr);
192    skt->res_attr.end = _PCMCIAAttr(skt->nr) + PCMCIAAttrSp - 1;
193    skt->res_attr.name = "attribute";
194    skt->res_attr.flags = IORESOURCE_MEM;
195
196    return soc_pcmcia_add_one(skt);
197}
198EXPORT_SYMBOL(sa11xx_drv_pcmcia_add_one);
199
200void sa11xx_drv_pcmcia_ops(struct pcmcia_low_level *ops)
201{
202    /*
203     * set default MECR calculation if the board specific
204     * code did not specify one...
205     */
206    if (!ops->get_timing)
207        ops->get_timing = sa1100_pcmcia_default_mecr_timing;
208
209    /* Provide our SA11x0 specific timing routines. */
210    ops->set_timing = sa1100_pcmcia_set_timing;
211    ops->show_timing = sa1100_pcmcia_show_timing;
212#ifdef CONFIG_CPU_FREQ
213    ops->frequency_change = sa1100_pcmcia_frequency_change;
214#endif
215}
216EXPORT_SYMBOL(sa11xx_drv_pcmcia_ops);
217
218int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops,
219                int first, int nr)
220{
221    struct skt_dev_info *sinfo;
222    struct soc_pcmcia_socket *skt;
223    int i, ret = 0;
224
225    sa11xx_drv_pcmcia_ops(ops);
226
227    sinfo = kzalloc(SKT_DEV_INFO_SIZE(nr), GFP_KERNEL);
228    if (!sinfo)
229        return -ENOMEM;
230
231    sinfo->nskt = nr;
232
233    /* Initialize processor specific parameters */
234    for (i = 0; i < nr; i++) {
235        skt = &sinfo->skt[i];
236
237        skt->nr = first + i;
238        soc_pcmcia_init_one(skt, ops, dev);
239
240        ret = sa11xx_drv_pcmcia_add_one(skt);
241        if (ret)
242            break;
243    }
244
245    if (ret) {
246        while (--i >= 0)
247            soc_pcmcia_remove_one(&sinfo->skt[i]);
248        kfree(sinfo);
249    } else {
250        dev_set_drvdata(dev, sinfo);
251    }
252
253    return ret;
254}
255EXPORT_SYMBOL(sa11xx_drv_pcmcia_probe);
256
257static int __init sa11xx_pcmcia_init(void)
258{
259    return 0;
260}
261fs_initcall(sa11xx_pcmcia_init);
262
263static void __exit sa11xx_pcmcia_exit(void) {}
264
265module_exit(sa11xx_pcmcia_exit);
266
267MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
268MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-11xx core socket driver");
269MODULE_LICENSE("Dual MPL/GPL");
270

Archive Download this file



interactive