linux-old/drivers/mtd/maps/dilnetpc.c
<<
>>
Prefs
   1/* dilnetpc.c -- MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP"
   2 *
   3 * This program is free software; you can redistribute it and/or modify
   4 * it under the terms of the GNU General Public License as published by
   5 * the Free Software Foundation; either version 2 of the License, or
   6 * (at your option) any later version.
   7 *
   8 * This program is distributed in the hope that it will be useful,
   9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11 * GNU General Public License for more details.
  12 *
  13 * You should have received a copy of the GNU General Public License
  14 * along with this program; if not, write to the Free Software
  15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  16 *
  17 * $Id: dilnetpc.c,v 1.8 2002/03/12 13:07:26 rkaiser Exp $
  18 *
  19 * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems
  20 * featuring the AMD Elan SC410 processor. There are two variants of this
  21 * board: DNP/1486 and ADNP/1486. The DNP version has 2 megs of flash
  22 * ROM (Intel 28F016S3) and 8 megs of DRAM, the ADNP version has 4 megs
  23 * flash and 16 megs of RAM.
  24 * For details, see http://www.ssv-embedded.de/ssv/pc104/p169.htm
  25 * and http://www.ssv-embedded.de/ssv/pc104/p170.htm
  26 */
  27
  28#include <linux/config.h>
  29#include <linux/module.h>
  30#include <linux/types.h>
  31#include <linux/kernel.h>
  32#include <asm/io.h>
  33#include <linux/mtd/mtd.h>
  34#include <linux/mtd/map.h>
  35#include <linux/mtd/partitions.h>
  36#include <linux/mtd/concat.h>
  37
  38/*
  39** The DIL/NetPC keeps it's BIOS in two distinct flash blocks.
  40** Destroying any of these blocks transforms the DNPC into
  41** a paperweight (albeit not a very useful one, considering
  42** it only weighs a few grams).
  43**
  44** Therefore, the BIOS blocks must never be erased or written to
  45** except by people who know exactly what they are doing (e.g.
  46** to install a BIOS update). These partitions are marked read-only
  47** by default, but can be made read/write by undefining
  48** DNPC_BIOS_BLOCKS_WRITEPROTECTED:
  49*/
  50#define DNPC_BIOS_BLOCKS_WRITEPROTECTED
  51
  52/*
  53** The ID string (in ROM) is checked to determine whether we
  54** are running on a DNP/1486 or ADNP/1486
  55*/
  56#define BIOSID_BASE     0x000fe100
  57
  58#define ID_DNPC "DNP1486"
  59#define ID_ADNP "ADNP1486"
  60
  61/*
  62** Address where the flash should appear in CPU space
  63*/
  64#define FLASH_BASE      0x2000000
  65
  66/*
  67** Chip Setup and Control (CSC) indexed register space
  68*/
  69#define CSC_INDEX       0x22
  70#define CSC_DATA        0x23
  71
  72#define CSC_MMSWAR      0x30    /* MMS window C-F attributes register */
  73#define CSC_MMSWDSR     0x31    /* MMS window C-F device select register */
  74
  75#define CSC_RBWR        0xa7    /* GPIO Read-Back/Write Register B */
  76
  77#define CSC_CR          0xd0    /* internal I/O device disable/Echo */
  78                                /* Z-bus/configuration register */
  79
  80#define CSC_PCCMDCR     0xf1    /* PC card mode and DMA control register */
  81
  82
  83/*
  84** PC Card indexed register space:
  85*/
  86
  87#define PCC_INDEX       0x3e0
  88#define PCC_DATA        0x3e1
  89
  90#define PCC_AWER_B              0x46    /* Socket B Address Window enable register */
  91#define PCC_MWSAR_1_Lo  0x58    /* memory window 1 start address low register */
  92#define PCC_MWSAR_1_Hi  0x59    /* memory window 1 start address high register */
  93#define PCC_MWEAR_1_Lo  0x5A    /* memory window 1 stop address low register */
  94#define PCC_MWEAR_1_Hi  0x5B    /* memory window 1 stop address high register */
  95#define PCC_MWAOR_1_Lo  0x5C    /* memory window 1 address offset low register */
  96#define PCC_MWAOR_1_Hi  0x5D    /* memory window 1 address offset high register */
  97
  98
  99/*
 100** Access to SC4x0's Chip Setup and Control (CSC)
 101** and PC Card (PCC) indexed registers:
 102*/
 103static inline void setcsc(int reg, unsigned char data)
 104{
 105        outb(reg, CSC_INDEX);
 106        outb(data, CSC_DATA);
 107}
 108
 109static inline unsigned char getcsc(int reg)
 110{
 111        outb(reg, CSC_INDEX);
 112        return(inb(CSC_DATA));
 113}
 114
 115static inline void setpcc(int reg, unsigned char data)
 116{
 117        outb(reg, PCC_INDEX);
 118        outb(data, PCC_DATA);
 119}
 120
 121static inline unsigned char getpcc(int reg)
 122{
 123        outb(reg, PCC_INDEX);
 124        return(inb(PCC_DATA));
 125}
 126
 127
 128/*
 129************************************************************
 130** Enable access to DIL/NetPC's flash by mapping it into
 131** the SC4x0's MMS Window C.
 132************************************************************
 133*/
 134static void dnpc_map_flash(unsigned long flash_base, unsigned long flash_size)
 135{
 136        unsigned long flash_end = flash_base + flash_size - 1;
 137
 138        /*
 139        ** enable setup of MMS windows C-F:
 140        */
 141        /* - enable PC Card indexed register space */
 142        setcsc(CSC_CR, getcsc(CSC_CR) | 0x2);
 143        /* - set PC Card controller to operate in standard mode */
 144        setcsc(CSC_PCCMDCR, getcsc(CSC_PCCMDCR) & ~1);
 145
 146        /*
 147        ** Program base address and end address of window
 148        ** where the flash ROM should appear in CPU address space
 149        */
 150        setpcc(PCC_MWSAR_1_Lo, (flash_base >> 12) & 0xff);
 151        setpcc(PCC_MWSAR_1_Hi, (flash_base >> 20) & 0x3f);
 152        setpcc(PCC_MWEAR_1_Lo, (flash_end >> 12) & 0xff);
 153        setpcc(PCC_MWEAR_1_Hi, (flash_end >> 20) & 0x3f);
 154
 155        /* program offset of first flash location to appear in this window (0) */
 156        setpcc(PCC_MWAOR_1_Lo, ((0 - flash_base) >> 12) & 0xff);
 157        setpcc(PCC_MWAOR_1_Hi, ((0 - flash_base)>> 20) & 0x3f);
 158
 159        /* set attributes for MMS window C: non-cacheable, write-enabled */
 160        setcsc(CSC_MMSWAR, getcsc(CSC_MMSWAR) & ~0x11);
 161
 162        /* select physical device ROMCS0 (i.e. flash) for MMS Window C */
 163        setcsc(CSC_MMSWDSR, getcsc(CSC_MMSWDSR) & ~0x03);
 164
 165        /* enable memory window 1 */
 166        setpcc(PCC_AWER_B, getpcc(PCC_AWER_B) | 0x02);
 167
 168        /* now disable PC Card indexed register space again */
 169        setcsc(CSC_CR, getcsc(CSC_CR) & ~0x2);
 170}
 171
 172
 173/*
 174************************************************************
 175** Disable access to DIL/NetPC's flash by mapping it into
 176** the SC4x0's MMS Window C.
 177************************************************************
 178*/
 179static void dnpc_unmap_flash(void)
 180{
 181        /* - enable PC Card indexed register space */
 182        setcsc(CSC_CR, getcsc(CSC_CR) | 0x2);
 183
 184        /* disable memory window 1 */
 185        setpcc(PCC_AWER_B, getpcc(PCC_AWER_B) & ~0x02);
 186
 187        /* now disable PC Card indexed register space again */
 188        setcsc(CSC_CR, getcsc(CSC_CR) & ~0x2);
 189}
 190
 191
 192static __u8 dnpc_read8(struct map_info *map, unsigned long ofs)
 193{
 194        return readb(map->map_priv_1 + ofs);
 195}
 196
 197static __u16 dnpc_read16(struct map_info *map, unsigned long ofs)
 198{
 199        return readw(map->map_priv_1 + ofs);
 200}
 201
 202static __u32 dnpc_read32(struct map_info *map, unsigned long ofs)
 203{
 204        return readl(map->map_priv_1 + ofs);
 205}
 206
 207static void dnpc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
 208{
 209        memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
 210}
 211
 212static void dnpc_write8(struct map_info *map, __u8 d, unsigned long adr)
 213{
 214        writeb(d, map->map_priv_1 + adr);
 215}
 216
 217static void dnpc_write16(struct map_info *map, __u16 d, unsigned long adr)
 218{
 219        writew(d, map->map_priv_1 + adr);
 220}
 221
 222static void dnpc_write32(struct map_info *map, __u32 d, unsigned long adr)
 223{
 224        writel(d, map->map_priv_1 + adr);
 225}
 226
 227static void dnpc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
 228{
 229        memcpy_toio((void *)(map->map_priv_1 + to), from, len);
 230}
 231
 232/*
 233************************************************************
 234** Enable/Disable VPP to write to flash
 235************************************************************
 236*/
 237
 238static spinlock_t dnpc_spin   = SPIN_LOCK_UNLOCKED;
 239static int        vpp_counter = 0;
 240/*
 241** This is what has to be done for the DNP board ..
 242*/
 243static void dnp_set_vpp(struct map_info *not_used, int on)
 244{
 245        spin_lock_irq(&dnpc_spin);
 246
 247        if (on)
 248        {
 249                if(++vpp_counter == 1)
 250                        setcsc(CSC_RBWR, getcsc(CSC_RBWR) & ~0x4);
 251        }
 252        else
 253        {
 254                if(--vpp_counter == 0)
 255                        setcsc(CSC_RBWR, getcsc(CSC_RBWR) | 0x4);
 256                else if(vpp_counter < 0)
 257                        BUG();
 258        }
 259        spin_unlock_irq(&dnpc_spin);
 260}
 261
 262/*
 263** .. and this the ADNP version:
 264*/
 265static void adnp_set_vpp(struct map_info *not_used, int on)
 266{
 267        spin_lock_irq(&dnpc_spin);
 268
 269        if (on)
 270        {
 271                if(++vpp_counter == 1)
 272                        setcsc(CSC_RBWR, getcsc(CSC_RBWR) & ~0x8);
 273        }
 274        else
 275        {
 276                if(--vpp_counter == 0)
 277                        setcsc(CSC_RBWR, getcsc(CSC_RBWR) | 0x8);
 278                else if(vpp_counter < 0)
 279                        BUG();
 280        }
 281        spin_unlock_irq(&dnpc_spin);
 282}
 283
 284
 285
 286#define DNP_WINDOW_SIZE         0x00200000      /*  DNP flash size is 2MiB  */
 287#define ADNP_WINDOW_SIZE        0x00400000      /* ADNP flash size is 4MiB */
 288#define WINDOW_ADDR                     FLASH_BASE
 289
 290static struct map_info dnpc_map = {
 291        name: "ADNP Flash Bank",
 292        size: ADNP_WINDOW_SIZE,
 293        buswidth: 1,
 294        read8: dnpc_read8,
 295        read16: dnpc_read16,
 296        read32: dnpc_read32,
 297        copy_from: dnpc_copy_from,
 298        write8: dnpc_write8,
 299        write16: dnpc_write16,
 300        write32: dnpc_write32,
 301        copy_to: dnpc_copy_to,
 302        set_vpp: adnp_set_vpp,
 303        map_priv_2: WINDOW_ADDR
 304};
 305
 306/*
 307** The layout of the flash is somewhat "strange":
 308**
 309** 1.  960 KiB (15 blocks) : Space for ROM Bootloader and user data
 310** 2.   64 KiB (1 block)   : System BIOS
 311** 3.  960 KiB (15 blocks) : User Data (DNP model) or
 312** 3. 3008 KiB (47 blocks) : User Data (ADNP model)
 313** 4.   64 KiB (1 block)   : System BIOS Entry
 314*/
 315
 316static struct mtd_partition partition_info[]=
 317{
 318        { 
 319                name:           "ADNP boot", 
 320                offset:         0, 
 321                size:           0xf0000,
 322        },
 323        { 
 324                name:           "ADNP system BIOS", 
 325                offset:         MTDPART_OFS_NXTBLK,
 326                size:           0x10000,
 327#ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
 328                mask_flags:     MTD_WRITEABLE,
 329#endif
 330        },
 331        {
 332                name:           "ADNP file system",
 333                offset:         MTDPART_OFS_NXTBLK,
 334                size:           0x2f0000,
 335        },
 336        {
 337                name:           "ADNP system BIOS entry", 
 338                offset:         MTDPART_OFS_NXTBLK,
 339                size:           MTDPART_SIZ_FULL,
 340#ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
 341                mask_flags:     MTD_WRITEABLE,
 342#endif
 343        },
 344};
 345
 346#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
 347
 348static struct mtd_info *mymtd;
 349static struct mtd_info *lowlvl_parts[NUM_PARTITIONS];
 350static struct mtd_info *merged_mtd;
 351
 352/*
 353** "Highlevel" partition info:
 354**
 355** Using the MTD concat layer, we can re-arrange partitions to our
 356** liking: we construct a virtual MTD device by concatenating the
 357** partitions, specifying the sequence such that the boot block
 358** is immediately followed by the filesystem block (i.e. the stupid
 359** system BIOS block is mapped to a different place). When re-partitioning
 360** this concatenated MTD device, we can set the boot block size to
 361** an arbitrary (though erase block aligned) value i.e. not one that
 362** is dictated by the flash's physical layout. We can thus set the
 363** boot block to be e.g. 64 KB (which is fully sufficient if we want
 364** to boot an etherboot image) or to -say- 1.5 MB if we want to boot
 365** a large kernel image. In all cases, the remainder of the flash
 366** is available as file system space.
 367*/
 368
 369static struct mtd_partition higlvl_partition_info[]=
 370{
 371        { 
 372                name:           "ADNP boot block", 
 373                offset:         0, 
 374                size:           CONFIG_MTD_DILNETPC_BOOTSIZE,
 375        },
 376        {
 377                name:           "ADNP file system space",
 378                offset:         MTDPART_OFS_NXTBLK,
 379                size:           ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000,
 380        },
 381        { 
 382                name:           "ADNP system BIOS + BIOS Entry", 
 383                offset:         MTDPART_OFS_NXTBLK,
 384                size:           MTDPART_SIZ_FULL,
 385#ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
 386                mask_flags:     MTD_WRITEABLE,
 387#endif
 388        },
 389};
 390
 391#define NUM_HIGHLVL_PARTITIONS (sizeof(higlvl_partition_info)/sizeof(partition_info[0]))
 392
 393
 394static int dnp_adnp_probe(void)
 395{
 396        char *biosid, rc = -1;
 397
 398        biosid = (char*)ioremap(BIOSID_BASE, 16);
 399        if(biosid)
 400        {
 401                if(!strcmp(biosid, ID_DNPC))
 402                        rc = 1;         /* this is a DNPC  */
 403                else if(!strcmp(biosid, ID_ADNP))
 404                        rc = 0;         /* this is a ADNPC */
 405        }
 406        iounmap((void *)biosid);
 407        return(rc);
 408}
 409
 410
 411static int __init init_dnpc(void)
 412{
 413        int is_dnp;
 414
 415        /*
 416        ** determine hardware (DNP/ADNP/invalid)
 417        */      
 418        if((is_dnp = dnp_adnp_probe()) < 0)
 419                return -ENXIO;
 420
 421        /*
 422        ** Things are set up for ADNP by default
 423        ** -> modify all that needs to be different for DNP
 424        */
 425        if(is_dnp)
 426        {       /*
 427                ** Adjust window size, select correct set_vpp function.
 428                ** The partitioning scheme is identical on both DNP
 429                ** and ADNP except for the size of the third partition.
 430                */
 431                int i;
 432                dnpc_map.size          = DNP_WINDOW_SIZE;
 433                dnpc_map.set_vpp       = dnp_set_vpp;
 434                partition_info[2].size = 0xf0000;
 435
 436                /*
 437                ** increment all string pointers so the leading 'A' gets skipped,
 438                ** thus turning all occurrences of "ADNP ..." into "DNP ..."
 439                */
 440                ++dnpc_map.name;
 441                for(i = 0; i < NUM_PARTITIONS; i++)
 442                        ++partition_info[i].name;
 443                higlvl_partition_info[1].size = DNP_WINDOW_SIZE - 
 444                        CONFIG_MTD_DILNETPC_BOOTSIZE - 0x20000;
 445                for(i = 0; i < NUM_HIGHLVL_PARTITIONS; i++)
 446                        ++higlvl_partition_info[i].name;
 447        }
 448
 449        printk(KERN_NOTICE "DIL/Net %s flash: 0x%lx at 0x%lx\n", 
 450                is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.map_priv_2);
 451
 452        dnpc_map.map_priv_1 = (unsigned long)ioremap_nocache(dnpc_map.map_priv_2, dnpc_map.size);
 453
 454        dnpc_map_flash(dnpc_map.map_priv_2, dnpc_map.size);
 455
 456        if (!dnpc_map.map_priv_1) {
 457                printk("Failed to ioremap_nocache\n");
 458                return -EIO;
 459        }
 460
 461        printk("FLASH virtual address: 0x%lx\n", dnpc_map.map_priv_1);
 462
 463        mymtd = do_map_probe("jedec_probe", &dnpc_map);
 464
 465        if (!mymtd)
 466                mymtd = do_map_probe("cfi_probe", &dnpc_map);
 467
 468        /*
 469        ** If flash probes fail, try to make flashes accessible
 470        ** at least as ROM. Ajust erasesize in this case since
 471        ** the default one (128M) will break our partitioning
 472        */
 473        if (!mymtd)
 474                if((mymtd = do_map_probe("map_rom", &dnpc_map)))
 475                        mymtd->erasesize = 0x10000;
 476
 477        if (!mymtd) {
 478                iounmap((void *)dnpc_map.map_priv_1);
 479                return -ENXIO;
 480        }
 481                
 482        mymtd->module = THIS_MODULE;
 483
 484        /*
 485        ** Supply pointers to lowlvl_parts[] array to add_mtd_partitions()
 486        ** -> add_mtd_partitions() will _not_ register MTD devices for
 487        ** the partitions, but will instead store pointers to the MTD
 488        ** objects it creates into our lowlvl_parts[] array.
 489        ** NOTE: we arrange the pointers such that the sequence of the
 490        **       partitions gets re-arranged: partition #2 follows
 491        **       partition #0.
 492        */
 493        partition_info[0].mtdp = &lowlvl_parts[0];
 494        partition_info[1].mtdp = &lowlvl_parts[2];
 495        partition_info[2].mtdp = &lowlvl_parts[1];
 496        partition_info[3].mtdp = &lowlvl_parts[3];
 497
 498        add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS);
 499
 500        /*
 501        ** now create a virtual MTD device by concatenating the for partitions
 502        ** (in the sequence given by the lowlvl_parts[] array.
 503        */
 504        merged_mtd = mtd_concat_create(lowlvl_parts, NUM_PARTITIONS, "(A)DNP Flash Concatenated");
 505        if(merged_mtd)
 506        {       /*
 507                ** now partition the new device the way we want it. This time,
 508                ** we do not supply mtd pointers in higlvl_partition_info, so
 509                ** add_mtd_partitions() will register the devices.
 510                */
 511                add_mtd_partitions(merged_mtd, higlvl_partition_info, NUM_HIGHLVL_PARTITIONS);
 512        }
 513
 514        return 0;
 515}
 516
 517static void __exit cleanup_dnpc(void)
 518{
 519        if(merged_mtd) {
 520                del_mtd_partitions(merged_mtd);
 521                mtd_concat_destroy(merged_mtd);
 522        }
 523
 524        if (mymtd) {
 525                del_mtd_partitions(mymtd);
 526                map_destroy(mymtd);
 527        }
 528        if (dnpc_map.map_priv_1) {
 529                iounmap((void *)dnpc_map.map_priv_1);
 530                dnpc_unmap_flash();
 531                dnpc_map.map_priv_1 = 0;
 532        }
 533}
 534
 535module_init(init_dnpc);
 536module_exit(cleanup_dnpc);
 537
 538MODULE_LICENSE("GPL");
 539MODULE_AUTHOR("Sysgo Real-Time Solutions GmbH");
 540MODULE_DESCRIPTION("MTD map driver for SSV DIL/NetPC DNP & ADNP");
 541