1*4882a593Smuzhiyun /*======================================================================
2*4882a593Smuzhiyun
3*4882a593Smuzhiyun Device driver for the PCMCIA control functionality of StrongARM
4*4882a593Smuzhiyun SA-1100 microprocessors.
5*4882a593Smuzhiyun
6*4882a593Smuzhiyun The contents of this file are subject to the Mozilla Public
7*4882a593Smuzhiyun License Version 1.1 (the "License"); you may not use this file
8*4882a593Smuzhiyun except in compliance with the License. You may obtain a copy of
9*4882a593Smuzhiyun the License at http://www.mozilla.org/MPL/
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun Software distributed under the License is distributed on an "AS
12*4882a593Smuzhiyun IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
13*4882a593Smuzhiyun implied. See the License for the specific language governing
14*4882a593Smuzhiyun rights and limitations under the License.
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun The initial developer of the original code is John G. Dorsey
17*4882a593Smuzhiyun <john+@cs.cmu.edu>. Portions created by John G. Dorsey are
18*4882a593Smuzhiyun Copyright (C) 1999 John G. Dorsey. All Rights Reserved.
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun Alternatively, the contents of this file may be used under the
21*4882a593Smuzhiyun terms of the GNU Public License version 2 (the "GPL"), in which
22*4882a593Smuzhiyun case the provisions of the GPL are applicable instead of the
23*4882a593Smuzhiyun above. If you wish to allow the use of your version of this file
24*4882a593Smuzhiyun only under the terms of the GPL and not to allow others to use
25*4882a593Smuzhiyun your version of this file under the MPL, indicate your decision
26*4882a593Smuzhiyun by deleting the provisions above and replace them with the notice
27*4882a593Smuzhiyun and other provisions required by the GPL. If you do not delete
28*4882a593Smuzhiyun the provisions above, a recipient may use your version of this
29*4882a593Smuzhiyun file under either the MPL or the GPL.
30*4882a593Smuzhiyun
31*4882a593Smuzhiyun ======================================================================*/
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun #include <linux/module.h>
34*4882a593Smuzhiyun #include <linux/init.h>
35*4882a593Smuzhiyun #include <linux/cpufreq.h>
36*4882a593Smuzhiyun #include <linux/ioport.h>
37*4882a593Smuzhiyun #include <linux/kernel.h>
38*4882a593Smuzhiyun #include <linux/spinlock.h>
39*4882a593Smuzhiyun #include <linux/io.h>
40*4882a593Smuzhiyun #include <linux/slab.h>
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun #include <mach/hardware.h>
43*4882a593Smuzhiyun #include <asm/irq.h>
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun #include "soc_common.h"
46*4882a593Smuzhiyun #include "sa11xx_base.h"
47*4882a593Smuzhiyun
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun /*
50*4882a593Smuzhiyun * sa1100_pcmcia_default_mecr_timing
51*4882a593Smuzhiyun * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
52*4882a593Smuzhiyun *
53*4882a593Smuzhiyun * Calculate MECR clock wait states for given CPU clock
54*4882a593Smuzhiyun * speed and command wait state. This function can be over-
55*4882a593Smuzhiyun * written by a board specific version.
56*4882a593Smuzhiyun *
57*4882a593Smuzhiyun * The default is to simply calculate the BS values as specified in
58*4882a593Smuzhiyun * the INTEL SA1100 development manual
59*4882a593Smuzhiyun * "Expansion Memory (PCMCIA) Configuration Register (MECR)"
60*4882a593Smuzhiyun * that's section 10.2.5 in _my_ version of the manual ;)
61*4882a593Smuzhiyun */
62*4882a593Smuzhiyun static unsigned int
sa1100_pcmcia_default_mecr_timing(struct soc_pcmcia_socket * skt,unsigned int cpu_speed,unsigned int cmd_time)63*4882a593Smuzhiyun sa1100_pcmcia_default_mecr_timing(struct soc_pcmcia_socket *skt,
64*4882a593Smuzhiyun unsigned int cpu_speed,
65*4882a593Smuzhiyun unsigned int cmd_time)
66*4882a593Smuzhiyun {
67*4882a593Smuzhiyun return sa1100_pcmcia_mecr_bs(cmd_time, cpu_speed);
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun /* sa1100_pcmcia_set_mecr()
71*4882a593Smuzhiyun * ^^^^^^^^^^^^^^^^^^^^^^^^
72*4882a593Smuzhiyun *
73*4882a593Smuzhiyun * set MECR value for socket <sock> based on this sockets
74*4882a593Smuzhiyun * io, mem and attribute space access speed.
75*4882a593Smuzhiyun * Call board specific BS value calculation to allow boards
76*4882a593Smuzhiyun * to tweak the BS values.
77*4882a593Smuzhiyun */
78*4882a593Smuzhiyun static int
sa1100_pcmcia_set_mecr(struct soc_pcmcia_socket * skt,unsigned int cpu_clock)79*4882a593Smuzhiyun sa1100_pcmcia_set_mecr(struct soc_pcmcia_socket *skt, unsigned int cpu_clock)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun struct soc_pcmcia_timing timing;
82*4882a593Smuzhiyun u32 mecr, old_mecr;
83*4882a593Smuzhiyun unsigned long flags;
84*4882a593Smuzhiyun unsigned int bs_io, bs_mem, bs_attr;
85*4882a593Smuzhiyun
86*4882a593Smuzhiyun soc_common_pcmcia_get_timing(skt, &timing);
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun bs_io = skt->ops->get_timing(skt, cpu_clock, timing.io);
89*4882a593Smuzhiyun bs_mem = skt->ops->get_timing(skt, cpu_clock, timing.mem);
90*4882a593Smuzhiyun bs_attr = skt->ops->get_timing(skt, cpu_clock, timing.attr);
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun local_irq_save(flags);
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun old_mecr = mecr = MECR;
95*4882a593Smuzhiyun MECR_FAST_SET(mecr, skt->nr, 0);
96*4882a593Smuzhiyun MECR_BSIO_SET(mecr, skt->nr, bs_io);
97*4882a593Smuzhiyun MECR_BSA_SET(mecr, skt->nr, bs_attr);
98*4882a593Smuzhiyun MECR_BSM_SET(mecr, skt->nr, bs_mem);
99*4882a593Smuzhiyun if (old_mecr != mecr)
100*4882a593Smuzhiyun MECR = mecr;
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun local_irq_restore(flags);
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun debug(skt, 2, "FAST %X BSM %X BSA %X BSIO %X\n",
105*4882a593Smuzhiyun MECR_FAST_GET(mecr, skt->nr),
106*4882a593Smuzhiyun MECR_BSM_GET(mecr, skt->nr), MECR_BSA_GET(mecr, skt->nr),
107*4882a593Smuzhiyun MECR_BSIO_GET(mecr, skt->nr));
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun return 0;
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun #ifdef CONFIG_CPU_FREQ
113*4882a593Smuzhiyun static int
sa1100_pcmcia_frequency_change(struct soc_pcmcia_socket * skt,unsigned long val,struct cpufreq_freqs * freqs)114*4882a593Smuzhiyun sa1100_pcmcia_frequency_change(struct soc_pcmcia_socket *skt,
115*4882a593Smuzhiyun unsigned long val,
116*4882a593Smuzhiyun struct cpufreq_freqs *freqs)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun switch (val) {
119*4882a593Smuzhiyun case CPUFREQ_PRECHANGE:
120*4882a593Smuzhiyun if (freqs->new > freqs->old)
121*4882a593Smuzhiyun sa1100_pcmcia_set_mecr(skt, freqs->new);
122*4882a593Smuzhiyun break;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun case CPUFREQ_POSTCHANGE:
125*4882a593Smuzhiyun if (freqs->new < freqs->old)
126*4882a593Smuzhiyun sa1100_pcmcia_set_mecr(skt, freqs->new);
127*4882a593Smuzhiyun break;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun return 0;
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun #endif
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun static int
sa1100_pcmcia_set_timing(struct soc_pcmcia_socket * skt)136*4882a593Smuzhiyun sa1100_pcmcia_set_timing(struct soc_pcmcia_socket *skt)
137*4882a593Smuzhiyun {
138*4882a593Smuzhiyun unsigned long clk = clk_get_rate(skt->clk);
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun return sa1100_pcmcia_set_mecr(skt, clk / 1000);
141*4882a593Smuzhiyun }
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun static int
sa1100_pcmcia_show_timing(struct soc_pcmcia_socket * skt,char * buf)144*4882a593Smuzhiyun sa1100_pcmcia_show_timing(struct soc_pcmcia_socket *skt, char *buf)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun struct soc_pcmcia_timing timing;
147*4882a593Smuzhiyun unsigned int clock = clk_get_rate(skt->clk) / 1000;
148*4882a593Smuzhiyun unsigned long mecr = MECR;
149*4882a593Smuzhiyun char *p = buf;
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun soc_common_pcmcia_get_timing(skt, &timing);
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun p+=sprintf(p, "I/O : %uns (%uns)\n", timing.io,
154*4882a593Smuzhiyun sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, skt->nr)));
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun p+=sprintf(p, "attribute: %uns (%uns)\n", timing.attr,
157*4882a593Smuzhiyun sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, skt->nr)));
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun p+=sprintf(p, "common : %uns (%uns)\n", timing.mem,
160*4882a593Smuzhiyun sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, skt->nr)));
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun return p - buf;
163*4882a593Smuzhiyun }
164*4882a593Smuzhiyun
165*4882a593Smuzhiyun static const char *skt_names[] = {
166*4882a593Smuzhiyun "PCMCIA socket 0",
167*4882a593Smuzhiyun "PCMCIA socket 1",
168*4882a593Smuzhiyun };
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun #define SKT_DEV_INFO_SIZE(n) \
171*4882a593Smuzhiyun (sizeof(struct skt_dev_info) + (n)*sizeof(struct soc_pcmcia_socket))
172*4882a593Smuzhiyun
sa11xx_drv_pcmcia_add_one(struct soc_pcmcia_socket * skt)173*4882a593Smuzhiyun int sa11xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt)
174*4882a593Smuzhiyun {
175*4882a593Smuzhiyun skt->res_skt.start = _PCMCIA(skt->nr);
176*4882a593Smuzhiyun skt->res_skt.end = _PCMCIA(skt->nr) + PCMCIASp - 1;
177*4882a593Smuzhiyun skt->res_skt.name = skt_names[skt->nr];
178*4882a593Smuzhiyun skt->res_skt.flags = IORESOURCE_MEM;
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun skt->res_io.start = _PCMCIAIO(skt->nr);
181*4882a593Smuzhiyun skt->res_io.end = _PCMCIAIO(skt->nr) + PCMCIAIOSp - 1;
182*4882a593Smuzhiyun skt->res_io.name = "io";
183*4882a593Smuzhiyun skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun skt->res_mem.start = _PCMCIAMem(skt->nr);
186*4882a593Smuzhiyun skt->res_mem.end = _PCMCIAMem(skt->nr) + PCMCIAMemSp - 1;
187*4882a593Smuzhiyun skt->res_mem.name = "memory";
188*4882a593Smuzhiyun skt->res_mem.flags = IORESOURCE_MEM;
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun skt->res_attr.start = _PCMCIAAttr(skt->nr);
191*4882a593Smuzhiyun skt->res_attr.end = _PCMCIAAttr(skt->nr) + PCMCIAAttrSp - 1;
192*4882a593Smuzhiyun skt->res_attr.name = "attribute";
193*4882a593Smuzhiyun skt->res_attr.flags = IORESOURCE_MEM;
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun return soc_pcmcia_add_one(skt);
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun EXPORT_SYMBOL(sa11xx_drv_pcmcia_add_one);
198*4882a593Smuzhiyun
sa11xx_drv_pcmcia_ops(struct pcmcia_low_level * ops)199*4882a593Smuzhiyun void sa11xx_drv_pcmcia_ops(struct pcmcia_low_level *ops)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun /*
202*4882a593Smuzhiyun * set default MECR calculation if the board specific
203*4882a593Smuzhiyun * code did not specify one...
204*4882a593Smuzhiyun */
205*4882a593Smuzhiyun if (!ops->get_timing)
206*4882a593Smuzhiyun ops->get_timing = sa1100_pcmcia_default_mecr_timing;
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun /* Provide our SA11x0 specific timing routines. */
209*4882a593Smuzhiyun ops->set_timing = sa1100_pcmcia_set_timing;
210*4882a593Smuzhiyun ops->show_timing = sa1100_pcmcia_show_timing;
211*4882a593Smuzhiyun #ifdef CONFIG_CPU_FREQ
212*4882a593Smuzhiyun ops->frequency_change = sa1100_pcmcia_frequency_change;
213*4882a593Smuzhiyun #endif
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun EXPORT_SYMBOL(sa11xx_drv_pcmcia_ops);
216*4882a593Smuzhiyun
sa11xx_drv_pcmcia_probe(struct device * dev,struct pcmcia_low_level * ops,int first,int nr)217*4882a593Smuzhiyun int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops,
218*4882a593Smuzhiyun int first, int nr)
219*4882a593Smuzhiyun {
220*4882a593Smuzhiyun struct skt_dev_info *sinfo;
221*4882a593Smuzhiyun struct soc_pcmcia_socket *skt;
222*4882a593Smuzhiyun int i, ret = 0;
223*4882a593Smuzhiyun struct clk *clk;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun clk = devm_clk_get(dev, NULL);
226*4882a593Smuzhiyun if (IS_ERR(clk))
227*4882a593Smuzhiyun return PTR_ERR(clk);
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun sa11xx_drv_pcmcia_ops(ops);
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun sinfo = devm_kzalloc(dev, SKT_DEV_INFO_SIZE(nr), GFP_KERNEL);
232*4882a593Smuzhiyun if (!sinfo)
233*4882a593Smuzhiyun return -ENOMEM;
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun sinfo->nskt = nr;
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun /* Initialize processor specific parameters */
238*4882a593Smuzhiyun for (i = 0; i < nr; i++) {
239*4882a593Smuzhiyun skt = &sinfo->skt[i];
240*4882a593Smuzhiyun
241*4882a593Smuzhiyun skt->nr = first + i;
242*4882a593Smuzhiyun skt->clk = clk;
243*4882a593Smuzhiyun soc_pcmcia_init_one(skt, ops, dev);
244*4882a593Smuzhiyun
245*4882a593Smuzhiyun ret = sa11xx_drv_pcmcia_add_one(skt);
246*4882a593Smuzhiyun if (ret)
247*4882a593Smuzhiyun break;
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun if (ret) {
251*4882a593Smuzhiyun while (--i >= 0)
252*4882a593Smuzhiyun soc_pcmcia_remove_one(&sinfo->skt[i]);
253*4882a593Smuzhiyun } else {
254*4882a593Smuzhiyun dev_set_drvdata(dev, sinfo);
255*4882a593Smuzhiyun }
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun return ret;
258*4882a593Smuzhiyun }
259*4882a593Smuzhiyun EXPORT_SYMBOL(sa11xx_drv_pcmcia_probe);
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
262*4882a593Smuzhiyun MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-11xx core socket driver");
263*4882a593Smuzhiyun MODULE_LICENSE("Dual MPL/GPL");
264