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

Archive Download this file



interactive