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