linux/drivers/mtd/maps/pci.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 *  linux/drivers/mtd/maps/pci.c
   4 *
   5 *  Copyright (C) 2001 Russell King, All rights reserved.
   6 *
   7 * Generic PCI memory map driver.  We support the following boards:
   8 *  - Intel IQ80310 ATU.
   9 *  - Intel EBSA285 (blank rom programming mode). Tested working 27/09/2001
  10 */
  11#include <linux/module.h>
  12#include <linux/kernel.h>
  13#include <linux/pci.h>
  14#include <linux/slab.h>
  15
  16#include <linux/mtd/mtd.h>
  17#include <linux/mtd/map.h>
  18#include <linux/mtd/partitions.h>
  19
  20struct map_pci_info;
  21
  22struct mtd_pci_info {
  23        int  (*init)(struct pci_dev *dev, struct map_pci_info *map);
  24        void (*exit)(struct pci_dev *dev, struct map_pci_info *map);
  25        unsigned long (*translate)(struct map_pci_info *map, unsigned long ofs);
  26        const char *map_name;
  27};
  28
  29struct map_pci_info {
  30        struct map_info map;
  31        void __iomem *base;
  32        void (*exit)(struct pci_dev *dev, struct map_pci_info *map);
  33        unsigned long (*translate)(struct map_pci_info *map, unsigned long ofs);
  34        struct pci_dev *dev;
  35};
  36
  37static map_word mtd_pci_read8(struct map_info *_map, unsigned long ofs)
  38{
  39        struct map_pci_info *map = (struct map_pci_info *)_map;
  40        map_word val;
  41        val.x[0]= readb(map->base + map->translate(map, ofs));
  42        return val;
  43}
  44
  45static map_word mtd_pci_read32(struct map_info *_map, unsigned long ofs)
  46{
  47        struct map_pci_info *map = (struct map_pci_info *)_map;
  48        map_word val;
  49        val.x[0] = readl(map->base + map->translate(map, ofs));
  50        return val;
  51}
  52
  53static void mtd_pci_copyfrom(struct map_info *_map, void *to, unsigned long from, ssize_t len)
  54{
  55        struct map_pci_info *map = (struct map_pci_info *)_map;
  56        memcpy_fromio(to, map->base + map->translate(map, from), len);
  57}
  58
  59static void mtd_pci_write8(struct map_info *_map, map_word val, unsigned long ofs)
  60{
  61        struct map_pci_info *map = (struct map_pci_info *)_map;
  62        writeb(val.x[0], map->base + map->translate(map, ofs));
  63}
  64
  65static void mtd_pci_write32(struct map_info *_map, map_word val, unsigned long ofs)
  66{
  67        struct map_pci_info *map = (struct map_pci_info *)_map;
  68        writel(val.x[0], map->base + map->translate(map, ofs));
  69}
  70
  71static void mtd_pci_copyto(struct map_info *_map, unsigned long to, const void *from, ssize_t len)
  72{
  73        struct map_pci_info *map = (struct map_pci_info *)_map;
  74        memcpy_toio(map->base + map->translate(map, to), from, len);
  75}
  76
  77static const struct map_info mtd_pci_map = {
  78        .phys =         NO_XIP,
  79        .copy_from =    mtd_pci_copyfrom,
  80        .copy_to =      mtd_pci_copyto,
  81};
  82
  83/*
  84 * Intel IOP80310 Flash driver
  85 */
  86
  87static int
  88intel_iq80310_init(struct pci_dev *dev, struct map_pci_info *map)
  89{
  90        u32 win_base;
  91
  92        map->map.bankwidth = 1;
  93        map->map.read = mtd_pci_read8;
  94        map->map.write = mtd_pci_write8;
  95
  96        map->map.size     = 0x00800000;
  97        map->base         = ioremap(pci_resource_start(dev, 0),
  98                                            pci_resource_len(dev, 0));
  99
 100        if (!map->base)
 101                return -ENOMEM;
 102
 103        /*
 104         * We want to base the memory window at Xscale
 105         * bus address 0, not 0x1000.
 106         */
 107        pci_read_config_dword(dev, 0x44, &win_base);
 108        pci_write_config_dword(dev, 0x44, 0);
 109
 110        map->map.map_priv_2 = win_base;
 111
 112        return 0;
 113}
 114
 115static void
 116intel_iq80310_exit(struct pci_dev *dev, struct map_pci_info *map)
 117{
 118        if (map->base)
 119                iounmap(map->base);
 120        pci_write_config_dword(dev, 0x44, map->map.map_priv_2);
 121}
 122
 123static unsigned long
 124intel_iq80310_translate(struct map_pci_info *map, unsigned long ofs)
 125{
 126        unsigned long page_addr = ofs & 0x00400000;
 127
 128        /*
 129         * This mundges the flash location so we avoid
 130         * the first 80 bytes (they appear to read nonsense).
 131         */
 132        if (page_addr) {
 133                writel(0x00000008, map->base + 0x1558);
 134                writel(0x00000000, map->base + 0x1550);
 135        } else {
 136                writel(0x00000007, map->base + 0x1558);
 137                writel(0x00800000, map->base + 0x1550);
 138                ofs += 0x00800000;
 139        }
 140
 141        return ofs;
 142}
 143
 144static struct mtd_pci_info intel_iq80310_info = {
 145        .init =         intel_iq80310_init,
 146        .exit =         intel_iq80310_exit,
 147        .translate =    intel_iq80310_translate,
 148        .map_name =     "cfi_probe",
 149};
 150
 151/*
 152 * Intel DC21285 driver
 153 */
 154
 155static int
 156intel_dc21285_init(struct pci_dev *dev, struct map_pci_info *map)
 157{
 158        unsigned long base, len;
 159
 160        base = pci_resource_start(dev, PCI_ROM_RESOURCE);
 161        len  = pci_resource_len(dev, PCI_ROM_RESOURCE);
 162
 163        if (!len || !base) {
 164                /*
 165                 * No ROM resource
 166                 */
 167                base = pci_resource_start(dev, 2);
 168                len  = pci_resource_len(dev, 2);
 169
 170                /*
 171                 * We need to re-allocate PCI BAR2 address range to the
 172                 * PCI ROM BAR, and disable PCI BAR2.
 173                 */
 174        } else {
 175                /*
 176                 * Hmm, if an address was allocated to the ROM resource, but
 177                 * not enabled, should we be allocating a new resource for it
 178                 * or simply enabling it?
 179                 */
 180                pci_enable_rom(dev);
 181                printk("%s: enabling expansion ROM\n", pci_name(dev));
 182        }
 183
 184        if (!len || !base)
 185                return -ENXIO;
 186
 187        map->map.bankwidth = 4;
 188        map->map.read = mtd_pci_read32;
 189        map->map.write = mtd_pci_write32;
 190        map->map.size     = len;
 191        map->base         = ioremap(base, len);
 192
 193        if (!map->base)
 194                return -ENOMEM;
 195
 196        return 0;
 197}
 198
 199static void
 200intel_dc21285_exit(struct pci_dev *dev, struct map_pci_info *map)
 201{
 202        if (map->base)
 203                iounmap(map->base);
 204
 205        /*
 206         * We need to undo the PCI BAR2/PCI ROM BAR address alteration.
 207         */
 208        pci_disable_rom(dev);
 209}
 210
 211static unsigned long
 212intel_dc21285_translate(struct map_pci_info *map, unsigned long ofs)
 213{
 214        return ofs & 0x00ffffc0 ? ofs : (ofs ^ (1 << 5));
 215}
 216
 217static struct mtd_pci_info intel_dc21285_info = {
 218        .init =         intel_dc21285_init,
 219        .exit =         intel_dc21285_exit,
 220        .translate =    intel_dc21285_translate,
 221        .map_name =     "jedec_probe",
 222};
 223
 224/*
 225 * PCI device ID table
 226 */
 227
 228static const struct pci_device_id mtd_pci_ids[] = {
 229        {
 230                .vendor =       PCI_VENDOR_ID_INTEL,
 231                .device =       0x530d,
 232                .subvendor =    PCI_ANY_ID,
 233                .subdevice =    PCI_ANY_ID,
 234                .class =        PCI_CLASS_MEMORY_OTHER << 8,
 235                .class_mask =   0xffff00,
 236                .driver_data =  (unsigned long)&intel_iq80310_info,
 237        },
 238        {
 239                .vendor =       PCI_VENDOR_ID_DEC,
 240                .device =       PCI_DEVICE_ID_DEC_21285,
 241                .subvendor =    0,      /* DC21285 defaults to 0 on reset */
 242                .subdevice =    0,      /* DC21285 defaults to 0 on reset */
 243                .driver_data =  (unsigned long)&intel_dc21285_info,
 244        },
 245        { 0, }
 246};
 247
 248/*
 249 * Generic code follows.
 250 */
 251
 252static int mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 253{
 254        struct mtd_pci_info *info = (struct mtd_pci_info *)id->driver_data;
 255        struct map_pci_info *map = NULL;
 256        struct mtd_info *mtd = NULL;
 257        int err;
 258
 259        err = pci_enable_device(dev);
 260        if (err)
 261                goto out;
 262
 263        err = pci_request_regions(dev, "pci mtd");
 264        if (err)
 265                goto out;
 266
 267        map = kmalloc(sizeof(*map), GFP_KERNEL);
 268        err = -ENOMEM;
 269        if (!map)
 270                goto release;
 271
 272        map->map       = mtd_pci_map;
 273        map->map.name  = pci_name(dev);
 274        map->dev       = dev;
 275        map->exit      = info->exit;
 276        map->translate = info->translate;
 277
 278        err = info->init(dev, map);
 279        if (err)
 280                goto release;
 281
 282        mtd = do_map_probe(info->map_name, &map->map);
 283        err = -ENODEV;
 284        if (!mtd)
 285                goto release;
 286
 287        mtd->owner = THIS_MODULE;
 288        mtd_device_register(mtd, NULL, 0);
 289
 290        pci_set_drvdata(dev, mtd);
 291
 292        return 0;
 293
 294release:
 295        if (map) {
 296                map->exit(dev, map);
 297                kfree(map);
 298        }
 299
 300        pci_release_regions(dev);
 301out:
 302        return err;
 303}
 304
 305static void mtd_pci_remove(struct pci_dev *dev)
 306{
 307        struct mtd_info *mtd = pci_get_drvdata(dev);
 308        struct map_pci_info *map = mtd->priv;
 309
 310        mtd_device_unregister(mtd);
 311        map_destroy(mtd);
 312        map->exit(dev, map);
 313        kfree(map);
 314
 315        pci_release_regions(dev);
 316}
 317
 318static struct pci_driver mtd_pci_driver = {
 319        .name =         "MTD PCI",
 320        .probe =        mtd_pci_probe,
 321        .remove =       mtd_pci_remove,
 322        .id_table =     mtd_pci_ids,
 323};
 324
 325module_pci_driver(mtd_pci_driver);
 326
 327MODULE_LICENSE("GPL");
 328MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
 329MODULE_DESCRIPTION("Generic PCI map driver");
 330MODULE_DEVICE_TABLE(pci, mtd_pci_ids);
 331