123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- /*======================================================================
- Device driver for the PCMCIA control functionality of StrongARM
- SA-1100 microprocessors.
- The contents of this file are subject to the Mozilla Public
- License Version 1.1 (the "License"); you may not use this file
- except in compliance with the License. You may obtain a copy of
- the License at http://www.mozilla.org/MPL/
- Software distributed under the License is distributed on an "AS
- IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- implied. See the License for the specific language governing
- rights and limitations under the License.
- The initial developer of the original code is John G. Dorsey
- <john+@cs.cmu.edu>. Portions created by John G. Dorsey are
- Copyright (C) 1999 John G. Dorsey. All Rights Reserved.
- Alternatively, the contents of this file may be used under the
- terms of the GNU Public License version 2 (the "GPL"), in which
- case the provisions of the GPL are applicable instead of the
- above. If you wish to allow the use of your version of this file
- only under the terms of the GPL and not to allow others to use
- your version of this file under the MPL, indicate your decision
- by deleting the provisions above and replace them with the notice
- and other provisions required by the GPL. If you do not delete
- the provisions above, a recipient may use your version of this
- file under either the MPL or the GPL.
- ======================================================================*/
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/cpufreq.h>
- #include <linux/ioport.h>
- #include <linux/kernel.h>
- #include <linux/spinlock.h>
- #include <linux/io.h>
- #include <linux/slab.h>
- #include <mach/hardware.h>
- #include <asm/irq.h>
- #include "soc_common.h"
- #include "sa11xx_base.h"
- /*
- * sa1100_pcmcia_default_mecr_timing
- * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- *
- * Calculate MECR clock wait states for given CPU clock
- * speed and command wait state. This function can be over-
- * written by a board specific version.
- *
- * The default is to simply calculate the BS values as specified in
- * the INTEL SA1100 development manual
- * "Expansion Memory (PCMCIA) Configuration Register (MECR)"
- * that's section 10.2.5 in _my_ version of the manual ;)
- */
- static unsigned int
- sa1100_pcmcia_default_mecr_timing(struct soc_pcmcia_socket *skt,
- unsigned int cpu_speed,
- unsigned int cmd_time)
- {
- return sa1100_pcmcia_mecr_bs(cmd_time, cpu_speed);
- }
- /* sa1100_pcmcia_set_mecr()
- * ^^^^^^^^^^^^^^^^^^^^^^^^
- *
- * set MECR value for socket <sock> based on this sockets
- * io, mem and attribute space access speed.
- * Call board specific BS value calculation to allow boards
- * to tweak the BS values.
- */
- static int
- sa1100_pcmcia_set_mecr(struct soc_pcmcia_socket *skt, unsigned int cpu_clock)
- {
- struct soc_pcmcia_timing timing;
- u32 mecr, old_mecr;
- unsigned long flags;
- unsigned int bs_io, bs_mem, bs_attr;
- soc_common_pcmcia_get_timing(skt, &timing);
- bs_io = skt->ops->get_timing(skt, cpu_clock, timing.io);
- bs_mem = skt->ops->get_timing(skt, cpu_clock, timing.mem);
- bs_attr = skt->ops->get_timing(skt, cpu_clock, timing.attr);
- local_irq_save(flags);
- old_mecr = mecr = MECR;
- MECR_FAST_SET(mecr, skt->nr, 0);
- MECR_BSIO_SET(mecr, skt->nr, bs_io);
- MECR_BSA_SET(mecr, skt->nr, bs_attr);
- MECR_BSM_SET(mecr, skt->nr, bs_mem);
- if (old_mecr != mecr)
- MECR = mecr;
- local_irq_restore(flags);
- debug(skt, 2, "FAST %X BSM %X BSA %X BSIO %X\n",
- MECR_FAST_GET(mecr, skt->nr),
- MECR_BSM_GET(mecr, skt->nr), MECR_BSA_GET(mecr, skt->nr),
- MECR_BSIO_GET(mecr, skt->nr));
- return 0;
- }
- #ifdef CONFIG_CPU_FREQ
- static int
- sa1100_pcmcia_frequency_change(struct soc_pcmcia_socket *skt,
- unsigned long val,
- struct cpufreq_freqs *freqs)
- {
- switch (val) {
- case CPUFREQ_PRECHANGE:
- if (freqs->new > freqs->old)
- sa1100_pcmcia_set_mecr(skt, freqs->new);
- break;
- case CPUFREQ_POSTCHANGE:
- if (freqs->new < freqs->old)
- sa1100_pcmcia_set_mecr(skt, freqs->new);
- break;
- }
- return 0;
- }
- #endif
- static int
- sa1100_pcmcia_set_timing(struct soc_pcmcia_socket *skt)
- {
- unsigned long clk = clk_get_rate(skt->clk);
- return sa1100_pcmcia_set_mecr(skt, clk / 1000);
- }
- static int
- sa1100_pcmcia_show_timing(struct soc_pcmcia_socket *skt, char *buf)
- {
- struct soc_pcmcia_timing timing;
- unsigned int clock = clk_get_rate(skt->clk);
- unsigned long mecr = MECR;
- char *p = buf;
- soc_common_pcmcia_get_timing(skt, &timing);
- p+=sprintf(p, "I/O : %u (%u)\n", timing.io,
- sa1100_pcmcia_cmd_time(clock, MECR_BSIO_GET(mecr, skt->nr)));
- p+=sprintf(p, "attribute: %u (%u)\n", timing.attr,
- sa1100_pcmcia_cmd_time(clock, MECR_BSA_GET(mecr, skt->nr)));
- p+=sprintf(p, "common : %u (%u)\n", timing.mem,
- sa1100_pcmcia_cmd_time(clock, MECR_BSM_GET(mecr, skt->nr)));
- return p - buf;
- }
- static const char *skt_names[] = {
- "PCMCIA socket 0",
- "PCMCIA socket 1",
- };
- #define SKT_DEV_INFO_SIZE(n) \
- (sizeof(struct skt_dev_info) + (n)*sizeof(struct soc_pcmcia_socket))
- int sa11xx_drv_pcmcia_add_one(struct soc_pcmcia_socket *skt)
- {
- skt->res_skt.start = _PCMCIA(skt->nr);
- skt->res_skt.end = _PCMCIA(skt->nr) + PCMCIASp - 1;
- skt->res_skt.name = skt_names[skt->nr];
- skt->res_skt.flags = IORESOURCE_MEM;
- skt->res_io.start = _PCMCIAIO(skt->nr);
- skt->res_io.end = _PCMCIAIO(skt->nr) + PCMCIAIOSp - 1;
- skt->res_io.name = "io";
- skt->res_io.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
- skt->res_mem.start = _PCMCIAMem(skt->nr);
- skt->res_mem.end = _PCMCIAMem(skt->nr) + PCMCIAMemSp - 1;
- skt->res_mem.name = "memory";
- skt->res_mem.flags = IORESOURCE_MEM;
- skt->res_attr.start = _PCMCIAAttr(skt->nr);
- skt->res_attr.end = _PCMCIAAttr(skt->nr) + PCMCIAAttrSp - 1;
- skt->res_attr.name = "attribute";
- skt->res_attr.flags = IORESOURCE_MEM;
- return soc_pcmcia_add_one(skt);
- }
- EXPORT_SYMBOL(sa11xx_drv_pcmcia_add_one);
- void sa11xx_drv_pcmcia_ops(struct pcmcia_low_level *ops)
- {
- /*
- * set default MECR calculation if the board specific
- * code did not specify one...
- */
- if (!ops->get_timing)
- ops->get_timing = sa1100_pcmcia_default_mecr_timing;
- /* Provide our SA11x0 specific timing routines. */
- ops->set_timing = sa1100_pcmcia_set_timing;
- ops->show_timing = sa1100_pcmcia_show_timing;
- #ifdef CONFIG_CPU_FREQ
- ops->frequency_change = sa1100_pcmcia_frequency_change;
- #endif
- }
- EXPORT_SYMBOL(sa11xx_drv_pcmcia_ops);
- int sa11xx_drv_pcmcia_probe(struct device *dev, struct pcmcia_low_level *ops,
- int first, int nr)
- {
- struct skt_dev_info *sinfo;
- struct soc_pcmcia_socket *skt;
- int i, ret = 0;
- struct clk *clk;
- clk = devm_clk_get(dev, NULL);
- if (IS_ERR(clk))
- return PTR_ERR(clk);
- sa11xx_drv_pcmcia_ops(ops);
- sinfo = devm_kzalloc(dev, SKT_DEV_INFO_SIZE(nr), GFP_KERNEL);
- if (!sinfo)
- return -ENOMEM;
- sinfo->nskt = nr;
- /* Initialize processor specific parameters */
- for (i = 0; i < nr; i++) {
- skt = &sinfo->skt[i];
- skt->nr = first + i;
- skt->clk = clk;
- soc_pcmcia_init_one(skt, ops, dev);
- ret = sa11xx_drv_pcmcia_add_one(skt);
- if (ret)
- break;
- }
- if (ret) {
- while (--i >= 0)
- soc_pcmcia_remove_one(&sinfo->skt[i]);
- } else {
- dev_set_drvdata(dev, sinfo);
- }
- return ret;
- }
- EXPORT_SYMBOL(sa11xx_drv_pcmcia_probe);
- MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
- MODULE_DESCRIPTION("Linux PCMCIA Card Services: SA-11xx core socket driver");
- MODULE_LICENSE("Dual MPL/GPL");
|