linux/drivers/mtd/maps/pmcmsp-flash.c
<<
>>
Prefs
   1/*
   2 * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions.
   3 * Config with both CFI and JEDEC device support.
   4 *
   5 * Basically physmap.c with the addition of partitions and
   6 * an array of mapping info to accommodate more than one flash type per board.
   7 *
   8 * Copyright 2005-2007 PMC-Sierra, Inc.
   9 *
  10 *  This program is free software; you can redistribute  it and/or modify it
  11 *  under  the terms of  the GNU General  Public License as published by the
  12 *  Free Software Foundation;  either version 2 of the  License, or (at your
  13 *  option) any later version.
  14 *
  15 *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
  16 *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
  17 *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
  18 *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
  19 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  20 *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
  21 *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  22 *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
  23 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  24 *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25 *
  26 *  You should have received a copy of the  GNU General Public License along
  27 *  with this program; if not, write  to the Free Software Foundation, Inc.,
  28 *  675 Mass Ave, Cambridge, MA 02139, USA.
  29 */
  30
  31#include <linux/slab.h>
  32#include <linux/module.h>
  33#include <linux/types.h>
  34#include <linux/kernel.h>
  35#include <linux/mtd/mtd.h>
  36#include <linux/mtd/map.h>
  37#include <linux/mtd/partitions.h>
  38
  39#include <asm/io.h>
  40
  41#include <msp_prom.h>
  42#include <msp_regs.h>
  43
  44
  45static struct mtd_info **msp_flash;
  46static struct mtd_partition **msp_parts;
  47static struct map_info *msp_maps;
  48static int fcnt;
  49
  50#define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n", __func__, __LINE__)
  51
  52static int __init init_msp_flash(void)
  53{
  54        int i, j, ret = -ENOMEM;
  55        int offset, coff;
  56        char *env;
  57        int pcnt;
  58        char flash_name[] = "flash0";
  59        char part_name[] = "flash0_0";
  60        unsigned addr, size;
  61
  62        /* If ELB is disabled by "ful-mux" mode, we can't get at flash */
  63        if ((*DEV_ID_REG & DEV_ID_SINGLE_PC) &&
  64            (*ELB_1PC_EN_REG & SINGLE_PCCARD)) {
  65                printk(KERN_NOTICE "Single PC Card mode: no flash access\n");
  66                return -ENXIO;
  67        }
  68
  69        /* examine the prom environment for flash devices */
  70        for (fcnt = 0; (env = prom_getenv(flash_name)); fcnt++)
  71                flash_name[5] = '0' + fcnt + 1;
  72
  73        if (fcnt < 1)
  74                return -ENXIO;
  75
  76        printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt);
  77
  78        msp_flash = kcalloc(fcnt, sizeof(*msp_flash), GFP_KERNEL);
  79        if (!msp_flash)
  80                return -ENOMEM;
  81
  82        msp_parts = kcalloc(fcnt, sizeof(*msp_parts), GFP_KERNEL);
  83        if (!msp_parts)
  84                goto free_msp_flash;
  85
  86        msp_maps = kcalloc(fcnt, sizeof(*msp_maps), GFP_KERNEL);
  87        if (!msp_maps)
  88                goto free_msp_parts;
  89
  90        /* loop over the flash devices, initializing each */
  91        for (i = 0; i < fcnt; i++) {
  92                /* examine the prom environment for flash partititions */
  93                part_name[5] = '0' + i;
  94                part_name[7] = '0';
  95                for (pcnt = 0; (env = prom_getenv(part_name)); pcnt++)
  96                        part_name[7] = '0' + pcnt + 1;
  97
  98                if (pcnt == 0) {
  99                        printk(KERN_NOTICE "Skipping flash device %d "
 100                                "(no partitions defined)\n", i);
 101                        continue;
 102                }
 103
 104                msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition),
 105                                       GFP_KERNEL);
 106                if (!msp_parts[i])
 107                        goto cleanup_loop;
 108
 109                /* now initialize the devices proper */
 110                flash_name[5] = '0' + i;
 111                env = prom_getenv(flash_name);
 112
 113                if (sscanf(env, "%x:%x", &addr, &size) < 2) {
 114                        ret = -ENXIO;
 115                        kfree(msp_parts[i]);
 116                        goto cleanup_loop;
 117                }
 118                addr = CPHYSADDR(addr);
 119
 120                printk(KERN_NOTICE
 121                        "MSP flash device \"%s\": 0x%08x at 0x%08x\n",
 122                        flash_name, size, addr);
 123                /* This must matchs the actual size of the flash chip */
 124                msp_maps[i].size = size;
 125                msp_maps[i].phys = addr;
 126
 127                /*
 128                 * Platforms have a specific limit of the size of memory
 129                 * which may be mapped for flash:
 130                 */
 131                if (size > CONFIG_MSP_FLASH_MAP_LIMIT)
 132                        size = CONFIG_MSP_FLASH_MAP_LIMIT;
 133
 134                msp_maps[i].virt = ioremap(addr, size);
 135                if (msp_maps[i].virt == NULL) {
 136                        ret = -ENXIO;
 137                        kfree(msp_parts[i]);
 138                        goto cleanup_loop;
 139                }
 140
 141                msp_maps[i].bankwidth = 1;
 142                msp_maps[i].name = kmalloc(7, GFP_KERNEL);
 143                if (!msp_maps[i].name) {
 144                        iounmap(msp_maps[i].virt);
 145                        kfree(msp_parts[i]);
 146                        goto cleanup_loop;
 147                }
 148
 149                msp_maps[i].name = strncpy(msp_maps[i].name, flash_name, 7);
 150
 151                for (j = 0; j < pcnt; j++) {
 152                        part_name[5] = '0' + i;
 153                        part_name[7] = '0' + j;
 154
 155                        env = prom_getenv(part_name);
 156
 157                        if (sscanf(env, "%x:%x:%n", &offset, &size,
 158                                                &coff) < 2) {
 159                                ret = -ENXIO;
 160                                kfree(msp_maps[i].name);
 161                                iounmap(msp_maps[i].virt);
 162                                kfree(msp_parts[i]);
 163                                goto cleanup_loop;
 164                        }
 165
 166                        msp_parts[i][j].size = size;
 167                        msp_parts[i][j].offset = offset;
 168                        msp_parts[i][j].name = env + coff;
 169                }
 170
 171                /* now probe and add the device */
 172                simple_map_init(&msp_maps[i]);
 173                msp_flash[i] = do_map_probe("cfi_probe", &msp_maps[i]);
 174                if (msp_flash[i]) {
 175                        msp_flash[i]->owner = THIS_MODULE;
 176                        mtd_device_register(msp_flash[i], msp_parts[i], pcnt);
 177                } else {
 178                        printk(KERN_ERR "map probe failed for flash\n");
 179                        ret = -ENXIO;
 180                        kfree(msp_maps[i].name);
 181                        iounmap(msp_maps[i].virt);
 182                        kfree(msp_parts[i]);
 183                        goto cleanup_loop;
 184                }
 185        }
 186
 187        return 0;
 188
 189cleanup_loop:
 190        while (i--) {
 191                mtd_device_unregister(msp_flash[i]);
 192                map_destroy(msp_flash[i]);
 193                kfree(msp_maps[i].name);
 194                iounmap(msp_maps[i].virt);
 195                kfree(msp_parts[i]);
 196        }
 197        kfree(msp_maps);
 198free_msp_parts:
 199        kfree(msp_parts);
 200free_msp_flash:
 201        kfree(msp_flash);
 202        return ret;
 203}
 204
 205static void __exit cleanup_msp_flash(void)
 206{
 207        int i;
 208
 209        for (i = 0; i < fcnt; i++) {
 210                mtd_device_unregister(msp_flash[i]);
 211                map_destroy(msp_flash[i]);
 212                iounmap((void *)msp_maps[i].virt);
 213
 214                /* free the memory */
 215                kfree(msp_maps[i].name);
 216                kfree(msp_parts[i]);
 217        }
 218
 219        kfree(msp_flash);
 220        kfree(msp_parts);
 221        kfree(msp_maps);
 222}
 223
 224MODULE_AUTHOR("PMC-Sierra, Inc");
 225MODULE_DESCRIPTION("MTD map driver for PMC-Sierra MSP boards");
 226MODULE_LICENSE("GPL");
 227
 228module_init(init_msp_flash);
 229module_exit(cleanup_msp_flash);
 230