linux/drivers/mtd/maps/ipaq-flash.c
<<
>>
Prefs
   1/*
   2 * Flash memory access on iPAQ Handhelds (either SA1100 or PXA250 based)
   3 *
   4 * (C) 2000 Nicolas Pitre <nico@cam.org>
   5 * (C) 2002 Hewlett-Packard Company <jamey.hicks@hp.com>
   6 * (C) 2003 Christian Pellegrin <chri@ascensit.com>, <chri@infis.univ.ts.it>: concatenation of multiple flashes
   7 *
   8 * $Id: ipaq-flash.c,v 1.5 2005/11/07 11:14:27 gleixner Exp $
   9 */
  10
  11#include <linux/module.h>
  12#include <linux/types.h>
  13#include <linux/kernel.h>
  14#include <linux/spinlock.h>
  15#include <linux/init.h>
  16#include <linux/slab.h>
  17#include <asm/page.h>
  18#include <asm/mach-types.h>
  19#include <asm/system.h>
  20#include <asm/errno.h>
  21
  22#include <linux/mtd/mtd.h>
  23#include <linux/mtd/map.h>
  24#include <linux/mtd/partitions.h>
  25#ifdef CONFIG_MTD_CONCAT
  26#include <linux/mtd/concat.h>
  27#endif
  28
  29#include <asm/hardware.h>
  30#include <asm/arch-sa1100/h3600.h>
  31#include <asm/io.h>
  32
  33
  34#ifndef CONFIG_IPAQ_HANDHELD
  35#error This is for iPAQ Handhelds only
  36#endif
  37#ifdef CONFIG_SA1100_JORNADA56X
  38
  39static void jornada56x_set_vpp(struct map_info *map, int vpp)
  40{
  41        if (vpp)
  42                GPSR = GPIO_GPIO26;
  43        else
  44                GPCR = GPIO_GPIO26;
  45        GPDR |= GPIO_GPIO26;
  46}
  47
  48#endif
  49
  50#ifdef CONFIG_SA1100_JORNADA720
  51
  52static void jornada720_set_vpp(struct map_info *map, int vpp)
  53{
  54        if (vpp)
  55                PPSR |= 0x80;
  56        else
  57                PPSR &= ~0x80;
  58        PPDR |= 0x80;
  59}
  60
  61#endif
  62
  63#define MAX_IPAQ_CS 2           /* Number of CS we are going to test */
  64
  65#define IPAQ_MAP_INIT(X) \
  66        { \
  67                name:           "IPAQ flash " X, \
  68        }
  69
  70
  71static struct map_info ipaq_map[MAX_IPAQ_CS] = {
  72        IPAQ_MAP_INIT("bank 1"),
  73        IPAQ_MAP_INIT("bank 2")
  74};
  75
  76static struct mtd_info *my_sub_mtd[MAX_IPAQ_CS] = {
  77        NULL,
  78        NULL
  79};
  80
  81/*
  82 * Here are partition information for all known IPAQ-based devices.
  83 * See include/linux/mtd/partitions.h for definition of the mtd_partition
  84 * structure.
  85 *
  86 * The *_max_flash_size is the maximum possible mapped flash size which
  87 * is not necessarily the actual flash size.  It must be no more than
  88 * the value specified in the "struct map_desc *_io_desc" mapping
  89 * definition for the corresponding machine.
  90 *
  91 * Please keep these in alphabetical order, and formatted as per existing
  92 * entries.  Thanks.
  93 */
  94
  95#ifdef CONFIG_IPAQ_HANDHELD
  96static unsigned long h3xxx_max_flash_size = 0x04000000;
  97static struct mtd_partition h3xxx_partitions[] = {
  98        {
  99                name:           "H3XXX boot firmware",
 100#ifndef CONFIG_LAB
 101                size:           0x00040000,
 102#else
 103                size:           0x00080000,
 104#endif
 105                offset:         0,
 106#ifndef CONFIG_LAB
 107                mask_flags:     MTD_WRITEABLE,  /* force read-only */
 108#endif
 109        },
 110        {
 111                name:           "H3XXX root jffs2",
 112#ifndef CONFIG_LAB
 113                size:           0x2000000 - 2*0x40000, /* Warning, this is fixed later */
 114                offset:         0x00040000,
 115#else
 116                size:           0x2000000 - 0x40000 - 0x80000, /* Warning, this is fixed later */
 117                offset:         0x00080000,
 118#endif
 119        },
 120        {
 121                name:           "asset",
 122                size:           0x40000,
 123                offset:         0x2000000 - 0x40000, /* Warning, this is fixed later */
 124                mask_flags:     MTD_WRITEABLE,  /* force read-only */
 125        }
 126};
 127
 128#ifndef CONFIG_MTD_CONCAT
 129static struct mtd_partition h3xxx_partitions_bank2[] = {
 130        /* this is used only on 2 CS machines when concat is not present */
 131        {
 132                name:           "second H3XXX root jffs2",
 133                size:           0x1000000 - 0x40000, /* Warning, this is fixed later */
 134                offset:         0x00000000,
 135        },
 136        {
 137                name:           "second asset",
 138                size:           0x40000,
 139                offset:         0x1000000 - 0x40000, /* Warning, this is fixed later */
 140                mask_flags:     MTD_WRITEABLE,  /* force read-only */
 141        }
 142};
 143#endif
 144
 145static DEFINE_SPINLOCK(ipaq_vpp_lock);
 146
 147static void h3xxx_set_vpp(struct map_info *map, int vpp)
 148{
 149        static int nest = 0;
 150
 151        spin_lock(&ipaq_vpp_lock);
 152        if (vpp)
 153                nest++;
 154        else
 155                nest--;
 156        if (nest)
 157                assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 1);
 158        else
 159                assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 0);
 160        spin_unlock(&ipaq_vpp_lock);
 161}
 162
 163#endif
 164
 165#if defined(CONFIG_SA1100_JORNADA56X) || defined(CONFIG_SA1100_JORNADA720)
 166static unsigned long jornada_max_flash_size = 0x02000000;
 167static struct mtd_partition jornada_partitions[] = {
 168        {
 169                name:           "Jornada boot firmware",
 170                size:           0x00040000,
 171                offset:         0,
 172                mask_flags:     MTD_WRITEABLE,  /* force read-only */
 173        }, {
 174                name:           "Jornada root jffs2",
 175                size:           MTDPART_SIZ_FULL,
 176                offset:         0x00040000,
 177        }
 178};
 179#endif
 180
 181
 182static struct mtd_partition *parsed_parts;
 183static struct mtd_info *mymtd;
 184
 185static unsigned long cs_phys[] = {
 186#ifdef CONFIG_ARCH_SA1100
 187        SA1100_CS0_PHYS,
 188        SA1100_CS1_PHYS,
 189        SA1100_CS2_PHYS,
 190        SA1100_CS3_PHYS,
 191        SA1100_CS4_PHYS,
 192        SA1100_CS5_PHYS,
 193#else
 194        PXA_CS0_PHYS,
 195        PXA_CS1_PHYS,
 196        PXA_CS2_PHYS,
 197        PXA_CS3_PHYS,
 198        PXA_CS4_PHYS,
 199        PXA_CS5_PHYS,
 200#endif
 201};
 202
 203static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
 204
 205static int __init h1900_special_case(void);
 206
 207int __init ipaq_mtd_init(void)
 208{
 209        struct mtd_partition *parts = NULL;
 210        int nb_parts = 0;
 211        int parsed_nr_parts = 0;
 212        const char *part_type;
 213        int i; /* used when we have >1 flash chips */
 214        unsigned long tot_flashsize = 0; /* used when we have >1 flash chips */
 215
 216        /* Default flash bankwidth */
 217        // ipaq_map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4;
 218
 219        if (machine_is_h1900())
 220        {
 221                /* For our intents, the h1900 is not a real iPAQ, so we special-case it. */
 222                return h1900_special_case();
 223        }
 224
 225        if (machine_is_h3100() || machine_is_h1900())
 226                for(i=0; i<MAX_IPAQ_CS; i++)
 227                        ipaq_map[i].bankwidth = 2;
 228        else
 229                for(i=0; i<MAX_IPAQ_CS; i++)
 230                        ipaq_map[i].bankwidth = 4;
 231
 232        /*
 233         * Static partition definition selection
 234         */
 235        part_type = "static";
 236
 237        simple_map_init(&ipaq_map[0]);
 238        simple_map_init(&ipaq_map[1]);
 239
 240#ifdef CONFIG_IPAQ_HANDHELD
 241        if (machine_is_ipaq()) {
 242                parts = h3xxx_partitions;
 243                nb_parts = ARRAY_SIZE(h3xxx_partitions);
 244                for(i=0; i<MAX_IPAQ_CS; i++) {
 245                        ipaq_map[i].size = h3xxx_max_flash_size;
 246                        ipaq_map[i].set_vpp = h3xxx_set_vpp;
 247                        ipaq_map[i].phys = cs_phys[i];
 248                        ipaq_map[i].virt = ioremap(cs_phys[i], 0x04000000);
 249                        if (machine_is_h3100 () || machine_is_h1900())
 250                                ipaq_map[i].bankwidth = 2;
 251                }
 252                if (machine_is_h3600()) {
 253                        /* No asset partition here */
 254                        h3xxx_partitions[1].size += 0x40000;
 255                        nb_parts--;
 256                }
 257        }
 258#endif
 259#ifdef CONFIG_ARCH_H5400
 260        if (machine_is_h5400()) {
 261                ipaq_map[0].size = 0x02000000;
 262                ipaq_map[1].size = 0x02000000;
 263                ipaq_map[1].phys = 0x02000000;
 264                ipaq_map[1].virt = ipaq_map[0].virt + 0x02000000;
 265        }
 266#endif
 267#ifdef CONFIG_ARCH_H1900
 268        if (machine_is_h1900()) {
 269                ipaq_map[0].size = 0x00400000;
 270                ipaq_map[1].size = 0x02000000;
 271                ipaq_map[1].phys = 0x00080000;
 272                ipaq_map[1].virt = ipaq_map[0].virt + 0x00080000;
 273        }
 274#endif
 275
 276#ifdef CONFIG_SA1100_JORNADA56X
 277        if (machine_is_jornada56x()) {
 278                parts = jornada_partitions;
 279                nb_parts = ARRAY_SIZE(jornada_partitions);
 280                ipaq_map[0].size = jornada_max_flash_size;
 281                ipaq_map[0].set_vpp = jornada56x_set_vpp;
 282                ipaq_map[0].virt = (__u32)ioremap(0x0, 0x04000000);
 283        }
 284#endif
 285#ifdef CONFIG_SA1100_JORNADA720
 286        if (machine_is_jornada720()) {
 287                parts = jornada_partitions;
 288                nb_parts = ARRAY_SIZE(jornada_partitions);
 289                ipaq_map[0].size = jornada_max_flash_size;
 290                ipaq_map[0].set_vpp = jornada720_set_vpp;
 291        }
 292#endif
 293
 294
 295        if (machine_is_ipaq()) { /* for iPAQs only */
 296                for(i=0; i<MAX_IPAQ_CS; i++) {
 297                        printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with CFI.\n", ipaq_map[i].bankwidth*8, ipaq_map[i].virt);
 298                        my_sub_mtd[i] = do_map_probe("cfi_probe", &ipaq_map[i]);
 299                        if (!my_sub_mtd[i]) {
 300                                printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with JEDEC.\n", ipaq_map[i].bankwidth*8, ipaq_map[i].virt);
 301                                my_sub_mtd[i] = do_map_probe("jedec_probe", &ipaq_map[i]);
 302                        }
 303                        if (!my_sub_mtd[i]) {
 304                                printk(KERN_NOTICE "iPAQ flash: failed to find flash.\n");
 305                                if (i)
 306                                        break;
 307                                else
 308                                        return -ENXIO;
 309                        } else
 310                                printk(KERN_NOTICE "iPAQ flash: found %d bytes\n", my_sub_mtd[i]->size);
 311
 312                        /* do we really need this debugging? --joshua 20030703 */
 313                        // printk("my_sub_mtd[%d]=%p\n", i, my_sub_mtd[i]);
 314                        my_sub_mtd[i]->owner = THIS_MODULE;
 315                        tot_flashsize += my_sub_mtd[i]->size;
 316                }
 317#ifdef CONFIG_MTD_CONCAT
 318                /* fix the asset location */
 319#       ifdef CONFIG_LAB
 320                h3xxx_partitions[1].size = tot_flashsize - 0x40000 - 0x80000 /* extra big boot block */;
 321#       else
 322                h3xxx_partitions[1].size = tot_flashsize - 2 * 0x40000;
 323#       endif
 324                h3xxx_partitions[2].offset = tot_flashsize - 0x40000;
 325                /* and concat the devices */
 326                mymtd = mtd_concat_create(&my_sub_mtd[0], i,
 327                                          "ipaq");
 328                if (!mymtd) {
 329                        printk("Cannot create iPAQ concat device\n");
 330                        return -ENXIO;
 331                }
 332#else
 333                mymtd = my_sub_mtd[0];
 334
 335                /*
 336                 *In the very near future, command line partition parsing
 337                 * will use the device name as 'mtd-id' instead of a value
 338                 * passed to the parse_cmdline_partitions() routine. Since
 339                 * the bootldr says 'ipaq', make sure it continues to work.
 340                 */
 341                mymtd->name = "ipaq";
 342
 343                if ((machine_is_h3600())) {
 344#       ifdef CONFIG_LAB
 345                        h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x80000;
 346#       else
 347                        h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000;
 348#       endif
 349                        nb_parts = 2;
 350                } else {
 351#       ifdef CONFIG_LAB
 352                        h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000 - 0x80000; /* extra big boot block */
 353#       else
 354                        h3xxx_partitions[1].size = my_sub_mtd[0]->size - 2*0x40000;
 355#       endif
 356                        h3xxx_partitions[2].offset = my_sub_mtd[0]->size - 0x40000;
 357                }
 358
 359                if (my_sub_mtd[1]) {
 360#       ifdef CONFIG_LAB
 361                        h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x80000;
 362#       else
 363                        h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x40000;
 364#       endif
 365                        h3xxx_partitions_bank2[1].offset = my_sub_mtd[1]->size - 0x40000;
 366                }
 367#endif
 368        }
 369        else {
 370                /*
 371                 * Now let's probe for the actual flash.  Do it here since
 372                 * specific machine settings might have been set above.
 373                 */
 374                printk(KERN_NOTICE "IPAQ flash: probing %d-bit flash bus, window=%lx\n", ipaq_map[0].bankwidth*8, ipaq_map[0].virt);
 375                mymtd = do_map_probe("cfi_probe", &ipaq_map[0]);
 376                if (!mymtd)
 377                        return -ENXIO;
 378                mymtd->owner = THIS_MODULE;
 379        }
 380
 381
 382        /*
 383         * Dynamic partition selection stuff (might override the static ones)
 384         */
 385
 386         i = parse_mtd_partitions(mymtd, part_probes, &parsed_parts, 0);
 387
 388         if (i > 0) {
 389                 nb_parts = parsed_nr_parts = i;
 390                 parts = parsed_parts;
 391                 part_type = "dynamic";
 392         }
 393
 394         if (!parts) {
 395                 printk(KERN_NOTICE "IPAQ flash: no partition info available, registering whole flash at once\n");
 396                 add_mtd_device(mymtd);
 397#ifndef CONFIG_MTD_CONCAT
 398                 if (my_sub_mtd[1])
 399                         add_mtd_device(my_sub_mtd[1]);
 400#endif
 401         } else {
 402                 printk(KERN_NOTICE "Using %s partition definition\n", part_type);
 403                 add_mtd_partitions(mymtd, parts, nb_parts);
 404#ifndef CONFIG_MTD_CONCAT
 405                 if (my_sub_mtd[1])
 406                         add_mtd_partitions(my_sub_mtd[1], h3xxx_partitions_bank2, ARRAY_SIZE(h3xxx_partitions_bank2));
 407#endif
 408         }
 409
 410         return 0;
 411}
 412
 413static void __exit ipaq_mtd_cleanup(void)
 414{
 415        int i;
 416
 417        if (mymtd) {
 418                del_mtd_partitions(mymtd);
 419#ifndef CONFIG_MTD_CONCAT
 420                if (my_sub_mtd[1])
 421                        del_mtd_partitions(my_sub_mtd[1]);
 422#endif
 423                map_destroy(mymtd);
 424#ifdef CONFIG_MTD_CONCAT
 425                for(i=0; i<MAX_IPAQ_CS; i++)
 426#else
 427                        for(i=1; i<MAX_IPAQ_CS; i++)
 428#endif
 429                        {
 430                                if (my_sub_mtd[i])
 431                                        map_destroy(my_sub_mtd[i]);
 432                        }
 433                kfree(parsed_parts);
 434        }
 435}
 436
 437static int __init h1900_special_case(void)
 438{
 439        /* The iPAQ h1900 is a special case - it has weird ROM. */
 440        simple_map_init(&ipaq_map[0]);
 441        ipaq_map[0].size = 0x80000;
 442        ipaq_map[0].set_vpp = h3xxx_set_vpp;
 443        ipaq_map[0].phys = 0x0;
 444        ipaq_map[0].virt = ioremap(0x0, 0x04000000);
 445        ipaq_map[0].bankwidth = 2;
 446
 447        printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with JEDEC.\n", ipaq_map[0].bankwidth*8, ipaq_map[0].virt);
 448        mymtd = do_map_probe("jedec_probe", &ipaq_map[0]);
 449        if (!mymtd)
 450                return -ENODEV;
 451        add_mtd_device(mymtd);
 452        printk(KERN_NOTICE "iPAQ flash: registered h1910 flash\n");
 453
 454        return 0;
 455}
 456
 457module_init(ipaq_mtd_init);
 458module_exit(ipaq_mtd_cleanup);
 459
 460MODULE_AUTHOR("Jamey Hicks");
 461MODULE_DESCRIPTION("IPAQ CFI map driver");
 462MODULE_LICENSE("MIT");
 463