xref: /OK3568_Linux_fs/kernel/drivers/pcmcia/sa11xx_base.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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