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