18c2ecf20Sopenharmony_ci/*======================================================================
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci    Device driver for the PCMCIA control functionality of StrongARM
48c2ecf20Sopenharmony_ci    SA-1100 microprocessors.
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci    The contents of this file are subject to the Mozilla Public
78c2ecf20Sopenharmony_ci    License Version 1.1 (the "License"); you may not use this file
88c2ecf20Sopenharmony_ci    except in compliance with the License. You may obtain a copy of
98c2ecf20Sopenharmony_ci    the License at http://www.mozilla.org/MPL/
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci    Software distributed under the License is distributed on an "AS
128c2ecf20Sopenharmony_ci    IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
138c2ecf20Sopenharmony_ci    implied. See the License for the specific language governing
148c2ecf20Sopenharmony_ci    rights and limitations under the License.
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci    The initial developer of the original code is John G. Dorsey
178c2ecf20Sopenharmony_ci    <john+@cs.cmu.edu>.  Portions created by John G. Dorsey are
188c2ecf20Sopenharmony_ci    Copyright (C) 1999 John G. Dorsey.  All Rights Reserved.
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci    Alternatively, the contents of this file may be used under the
218c2ecf20Sopenharmony_ci    terms of the GNU Public License version 2 (the "GPL"), in which
228c2ecf20Sopenharmony_ci    case the provisions of the GPL are applicable instead of the
238c2ecf20Sopenharmony_ci    above.  If you wish to allow the use of your version of this file
248c2ecf20Sopenharmony_ci    only under the terms of the GPL and not to allow others to use
258c2ecf20Sopenharmony_ci    your version of this file under the MPL, indicate your decision
268c2ecf20Sopenharmony_ci    by deleting the provisions above and replace them with the notice
278c2ecf20Sopenharmony_ci    and other provisions required by the GPL.  If you do not delete
288c2ecf20Sopenharmony_ci    the provisions above, a recipient may use your version of this
298c2ecf20Sopenharmony_ci    file under either the MPL or the GPL.
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci======================================================================*/
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <linux/module.h>
348c2ecf20Sopenharmony_ci#include <linux/init.h>
358c2ecf20Sopenharmony_ci#include <linux/cpufreq.h>
368c2ecf20Sopenharmony_ci#include <linux/ioport.h>
378c2ecf20Sopenharmony_ci#include <linux/kernel.h>
388c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
398c2ecf20Sopenharmony_ci#include <linux/io.h>
408c2ecf20Sopenharmony_ci#include <linux/slab.h>
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci#include <mach/hardware.h>
438c2ecf20Sopenharmony_ci#include <asm/irq.h>
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci#include "soc_common.h"
468c2ecf20Sopenharmony_ci#include "sa11xx_base.h"
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/*
508c2ecf20Sopenharmony_ci * sa1100_pcmcia_default_mecr_timing
518c2ecf20Sopenharmony_ci * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
528c2ecf20Sopenharmony_ci *
538c2ecf20Sopenharmony_ci * Calculate MECR clock wait states for given CPU clock
548c2ecf20Sopenharmony_ci * speed and command wait state. This function can be over-
558c2ecf20Sopenharmony_ci * written by a board specific version.
568c2ecf20Sopenharmony_ci *
578c2ecf20Sopenharmony_ci * The default is to simply calculate the BS values as specified in
588c2ecf20Sopenharmony_ci * the INTEL SA1100 development manual
598c2ecf20Sopenharmony_ci * "Expansion Memory (PCMCIA) Configuration Register (MECR)"
608c2ecf20Sopenharmony_ci * that's section 10.2.5 in _my_ version of the manual ;)
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_cistatic unsigned int
638c2ecf20Sopenharmony_cisa1100_pcmcia_default_mecr_timing(struct soc_pcmcia_socket *skt,
648c2ecf20Sopenharmony_ci				  unsigned int cpu_speed,
658c2ecf20Sopenharmony_ci				  unsigned int cmd_time)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	return sa1100_pcmcia_mecr_bs(cmd_time, cpu_speed);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci/* sa1100_pcmcia_set_mecr()
718c2ecf20Sopenharmony_ci * ^^^^^^^^^^^^^^^^^^^^^^^^
728c2ecf20Sopenharmony_ci *
738c2ecf20Sopenharmony_ci * set MECR value for socket <sock> based on this sockets
748c2ecf20Sopenharmony_ci * io, mem and attribute space access speed.
758c2ecf20Sopenharmony_ci * Call board specific BS value calculation to allow boards
768c2ecf20Sopenharmony_ci * to tweak the BS values.
778c2ecf20Sopenharmony_ci */
788c2ecf20Sopenharmony_cistatic int
798c2ecf20Sopenharmony_cisa1100_pcmcia_set_mecr(struct soc_pcmcia_socket *skt, unsigned int cpu_clock)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	struct soc_pcmcia_timing timing;
828c2ecf20Sopenharmony_ci	u32 mecr, old_mecr;
838c2ecf20Sopenharmony_ci	unsigned long flags;
848c2ecf20Sopenharmony_ci	unsigned int bs_io, bs_mem, bs_attr;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	soc_common_pcmcia_get_timing(skt, &timing);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	bs_io = skt->ops->get_timing(skt, cpu_clock, timing.io);
898c2ecf20Sopenharmony_ci	bs_mem = skt->ops->get_timing(skt, cpu_clock, timing.mem);
908c2ecf20Sopenharmony_ci	bs_attr = skt->ops->get_timing(skt, cpu_clock, timing.attr);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	local_irq_save(flags);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	old_mecr = mecr = MECR;
958c2ecf20Sopenharmony_ci	MECR_FAST_SET(mecr, skt->nr, 0);
968c2ecf20Sopenharmony_ci	MECR_BSIO_SET(mecr, skt->nr, bs_io);
978c2ecf20Sopenharmony_ci	MECR_BSA_SET(mecr, skt->nr, bs_attr);
988c2ecf20Sopenharmony_ci	MECR_BSM_SET(mecr, skt->nr, bs_mem);
998c2ecf20Sopenharmony_ci	if (old_mecr != mecr)
1008c2ecf20Sopenharmony_ci		MECR = mecr;
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	local_irq_restore(flags);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	debug(skt, 2, "FAST %X  BSM %X  BSA %X  BSIO %X\n",
1058c2ecf20Sopenharmony_ci	      MECR_FAST_GET(mecr, skt->nr),
1068c2ecf20Sopenharmony_ci	      MECR_BSM_GET(mecr, skt->nr), MECR_BSA_GET(mecr, skt->nr),
1078c2ecf20Sopenharmony_ci	      MECR_BSIO_GET(mecr, skt->nr));
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return 0;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_FREQ
1138c2ecf20Sopenharmony_cistatic int
1148c2ecf20Sopenharmony_cisa1100_pcmcia_frequency_change(struct soc_pcmcia_socket *skt,
1158c2ecf20Sopenharmony_ci			       unsigned long val,
1168c2ecf20Sopenharmony_ci			       struct cpufreq_freqs *freqs)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	switch (val) {
1198c2ecf20Sopenharmony_ci	case CPUFREQ_PRECHANGE:
1208c2ecf20Sopenharmony_ci		if (freqs->new > freqs->old)
1218c2ecf20Sopenharmony_ci			sa1100_pcmcia_set_mecr(skt, freqs->new);
1228c2ecf20Sopenharmony_ci		break;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	case CPUFREQ_POSTCHANGE:
1258c2ecf20Sopenharmony_ci		if (freqs->new < freqs->old)
1268c2ecf20Sopenharmony_ci			sa1100_pcmcia_set_mecr(skt, freqs->new);
1278c2ecf20Sopenharmony_ci		break;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	return 0;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci#endif
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cistatic int
1368c2ecf20Sopenharmony_cisa1100_pcmcia_set_timing(struct soc_pcmcia_socket *skt)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	unsigned long clk = clk_get_rate(skt->clk);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	return sa1100_pcmcia_set_mecr(skt, clk / 1000);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_cistatic int
1448c2ecf20Sopenharmony_cisa1100_pcmcia_show_timing(struct soc_pcmcia_socket *skt, char *buf)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	struct soc_pcmcia_timing timing;
1478c2ecf20Sopenharmony_ci	unsigned int clock = clk_get_rate(skt->clk) / 1000;
1488c2ecf20Sopenharmony_ci	unsigned long mecr = MECR;
1498c2ecf20Sopenharmony_ci	char *p = buf;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	soc_common_pcmcia_get_timing(skt, &timing);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	p+=sprintf(p, "I/O      : %uns (%uns)\n", timing.io,
1548c2ecf20Sopenharmony_ci		   sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, skt->nr)));
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	p+=sprintf(p, "attribute: %uns (%uns)\n", timing.attr,
1578c2ecf20Sopenharmony_ci		   sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, skt->nr)));
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	p+=sprintf(p, "common   : %uns (%uns)\n", timing.mem,
1608c2ecf20Sopenharmony_ci		   sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, skt->nr)));
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return p - buf;
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic const char *skt_names[] = {
1668c2ecf20Sopenharmony_ci	"PCMCIA socket 0",
1678c2ecf20Sopenharmony_ci	"PCMCIA socket 1",
1688c2ecf20Sopenharmony_ci};
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci#define SKT_DEV_INFO_SIZE(n) \
1718c2ecf20Sopenharmony_ci	(sizeof(struct skt_dev_info) + (n)*sizeof(struct soc_pcmcia_socket))
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ciint sa11xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	skt->res_skt.start = _PCMCIA(skt->nr);
1768c2ecf20Sopenharmony_ci	skt->res_skt.end = _PCMCIA(skt->nr) + PCMCIASp - 1;
1778c2ecf20Sopenharmony_ci	skt->res_skt.name = skt_names[skt->nr];
1788c2ecf20Sopenharmony_ci	skt->res_skt.flags = IORESOURCE_MEM;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	skt->res_io.start = _PCMCIAIO(skt->nr);
1818c2ecf20Sopenharmony_ci	skt->res_io.end = _PCMCIAIO(skt->nr) + PCMCIAIOSp - 1;
1828c2ecf20Sopenharmony_ci	skt->res_io.name = "io";
1838c2ecf20Sopenharmony_ci	skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	skt->res_mem.start = _PCMCIAMem(skt->nr);
1868c2ecf20Sopenharmony_ci	skt->res_mem.end = _PCMCIAMem(skt->nr) + PCMCIAMemSp - 1;
1878c2ecf20Sopenharmony_ci	skt->res_mem.name = "memory";
1888c2ecf20Sopenharmony_ci	skt->res_mem.flags = IORESOURCE_MEM;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	skt->res_attr.start = _PCMCIAAttr(skt->nr);
1918c2ecf20Sopenharmony_ci	skt->res_attr.end = _PCMCIAAttr(skt->nr) + PCMCIAAttrSp - 1;
1928c2ecf20Sopenharmony_ci	skt->res_attr.name = "attribute";
1938c2ecf20Sopenharmony_ci	skt->res_attr.flags = IORESOURCE_MEM;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	return soc_pcmcia_add_one(skt);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sa11xx_drv_pcmcia_add_one);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_civoid sa11xx_drv_pcmcia_ops(struct pcmcia_low_level *ops)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	/*
2028c2ecf20Sopenharmony_ci	 * set default MECR calculation if the board specific
2038c2ecf20Sopenharmony_ci	 * code did not specify one...
2048c2ecf20Sopenharmony_ci	 */
2058c2ecf20Sopenharmony_ci	if (!ops->get_timing)
2068c2ecf20Sopenharmony_ci		ops->get_timing = sa1100_pcmcia_default_mecr_timing;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/* Provide our SA11x0 specific timing routines. */
2098c2ecf20Sopenharmony_ci	ops->set_timing  = sa1100_pcmcia_set_timing;
2108c2ecf20Sopenharmony_ci	ops->show_timing = sa1100_pcmcia_show_timing;
2118c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_FREQ
2128c2ecf20Sopenharmony_ci	ops->frequency_change = sa1100_pcmcia_frequency_change;
2138c2ecf20Sopenharmony_ci#endif
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sa11xx_drv_pcmcia_ops);
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ciint sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops,
2188c2ecf20Sopenharmony_ci			    int first, int nr)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	struct skt_dev_info *sinfo;
2218c2ecf20Sopenharmony_ci	struct soc_pcmcia_socket *skt;
2228c2ecf20Sopenharmony_ci	int i, ret = 0;
2238c2ecf20Sopenharmony_ci	struct clk *clk;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	clk = devm_clk_get(dev, NULL);
2268c2ecf20Sopenharmony_ci	if (IS_ERR(clk))
2278c2ecf20Sopenharmony_ci		return PTR_ERR(clk);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	sa11xx_drv_pcmcia_ops(ops);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	sinfo = devm_kzalloc(dev, SKT_DEV_INFO_SIZE(nr), GFP_KERNEL);
2328c2ecf20Sopenharmony_ci	if (!sinfo)
2338c2ecf20Sopenharmony_ci		return -ENOMEM;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	sinfo->nskt = nr;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	/* Initialize processor specific parameters */
2388c2ecf20Sopenharmony_ci	for (i = 0; i < nr; i++) {
2398c2ecf20Sopenharmony_ci		skt = &sinfo->skt[i];
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci		skt->nr = first + i;
2428c2ecf20Sopenharmony_ci		skt->clk = clk;
2438c2ecf20Sopenharmony_ci		soc_pcmcia_init_one(skt, ops, dev);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci		ret = sa11xx_drv_pcmcia_add_one(skt);
2468c2ecf20Sopenharmony_ci		if (ret)
2478c2ecf20Sopenharmony_ci			break;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (ret) {
2518c2ecf20Sopenharmony_ci		while (--i >= 0)
2528c2ecf20Sopenharmony_ci			soc_pcmcia_remove_one(&sinfo->skt[i]);
2538c2ecf20Sopenharmony_ci	} else {
2548c2ecf20Sopenharmony_ci		dev_set_drvdata(dev, sinfo);
2558c2ecf20Sopenharmony_ci	}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return ret;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ciEXPORT_SYMBOL(sa11xx_drv_pcmcia_probe);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ciMODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
2628c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-11xx core socket driver");
2638c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual MPL/GPL");
264