Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * esb2rom.c
0004  *
0005  * Normal mappings of flash chips in physical memory
0006  * through the Intel ESB2 Southbridge.
0007  *
0008  * This was derived from ichxrom.c in May 2006 by
0009  *  Lew Glendenning <lglendenning@lnxi.com>
0010  *
0011  * Eric Biederman, of course, was a major help in this effort.
0012  */
0013 
0014 #include <linux/module.h>
0015 #include <linux/types.h>
0016 #include <linux/kernel.h>
0017 #include <linux/init.h>
0018 #include <linux/slab.h>
0019 #include <asm/io.h>
0020 #include <linux/mtd/mtd.h>
0021 #include <linux/mtd/map.h>
0022 #include <linux/mtd/cfi.h>
0023 #include <linux/mtd/flashchip.h>
0024 #include <linux/pci.h>
0025 #include <linux/pci_ids.h>
0026 #include <linux/list.h>
0027 
0028 #define MOD_NAME KBUILD_BASENAME
0029 
0030 #define ADDRESS_NAME_LEN 18
0031 
0032 #define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */
0033 
0034 #define BIOS_CNTL       0xDC
0035 #define BIOS_LOCK_ENABLE    0x02
0036 #define BIOS_WRITE_ENABLE   0x01
0037 
0038 /* This became a 16-bit register, and EN2 has disappeared */
0039 #define FWH_DEC_EN1 0xD8
0040 #define FWH_F8_EN   0x8000
0041 #define FWH_F0_EN   0x4000
0042 #define FWH_E8_EN   0x2000
0043 #define FWH_E0_EN   0x1000
0044 #define FWH_D8_EN   0x0800
0045 #define FWH_D0_EN   0x0400
0046 #define FWH_C8_EN   0x0200
0047 #define FWH_C0_EN   0x0100
0048 #define FWH_LEGACY_F_EN 0x0080
0049 #define FWH_LEGACY_E_EN 0x0040
0050 /* reserved  0x0020 and 0x0010 */
0051 #define FWH_70_EN   0x0008
0052 #define FWH_60_EN   0x0004
0053 #define FWH_50_EN   0x0002
0054 #define FWH_40_EN   0x0001
0055 
0056 /* these are 32-bit values */
0057 #define FWH_SEL1    0xD0
0058 #define FWH_SEL2    0xD4
0059 
0060 #define FWH_8MiB    (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
0061              FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
0062              FWH_70_EN | FWH_60_EN | FWH_50_EN | FWH_40_EN)
0063 
0064 #define FWH_7MiB    (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
0065              FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
0066              FWH_70_EN | FWH_60_EN | FWH_50_EN)
0067 
0068 #define FWH_6MiB    (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
0069              FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
0070              FWH_70_EN | FWH_60_EN)
0071 
0072 #define FWH_5MiB    (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
0073              FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
0074              FWH_70_EN)
0075 
0076 #define FWH_4MiB    (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
0077              FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN)
0078 
0079 #define FWH_3_5MiB  (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
0080              FWH_D8_EN | FWH_D0_EN | FWH_C8_EN)
0081 
0082 #define FWH_3MiB    (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
0083              FWH_D8_EN | FWH_D0_EN)
0084 
0085 #define FWH_2_5MiB  (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
0086              FWH_D8_EN)
0087 
0088 #define FWH_2MiB    (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN)
0089 
0090 #define FWH_1_5MiB  (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN)
0091 
0092 #define FWH_1MiB    (FWH_F8_EN | FWH_F0_EN)
0093 
0094 #define FWH_0_5MiB  (FWH_F8_EN)
0095 
0096 
0097 struct esb2rom_window {
0098     void __iomem* virt;
0099     unsigned long phys;
0100     unsigned long size;
0101     struct list_head maps;
0102     struct resource rsrc;
0103     struct pci_dev *pdev;
0104 };
0105 
0106 struct esb2rom_map_info {
0107     struct list_head list;
0108     struct map_info map;
0109     struct mtd_info *mtd;
0110     struct resource rsrc;
0111     char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
0112 };
0113 
0114 static struct esb2rom_window esb2rom_window = {
0115     .maps = LIST_HEAD_INIT(esb2rom_window.maps),
0116 };
0117 
0118 static void esb2rom_cleanup(struct esb2rom_window *window)
0119 {
0120     struct esb2rom_map_info *map, *scratch;
0121     u8 byte;
0122 
0123     /* Disable writes through the rom window */
0124     pci_read_config_byte(window->pdev, BIOS_CNTL, &byte);
0125     pci_write_config_byte(window->pdev, BIOS_CNTL,
0126         byte & ~BIOS_WRITE_ENABLE);
0127 
0128     /* Free all of the mtd devices */
0129     list_for_each_entry_safe(map, scratch, &window->maps, list) {
0130         if (map->rsrc.parent)
0131             release_resource(&map->rsrc);
0132         mtd_device_unregister(map->mtd);
0133         map_destroy(map->mtd);
0134         list_del(&map->list);
0135         kfree(map);
0136     }
0137     if (window->rsrc.parent)
0138         release_resource(&window->rsrc);
0139     if (window->virt) {
0140         iounmap(window->virt);
0141         window->virt = NULL;
0142         window->phys = 0;
0143         window->size = 0;
0144     }
0145     pci_dev_put(window->pdev);
0146 }
0147 
0148 static int __init esb2rom_init_one(struct pci_dev *pdev,
0149                    const struct pci_device_id *ent)
0150 {
0151     static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
0152     struct esb2rom_window *window = &esb2rom_window;
0153     struct esb2rom_map_info *map = NULL;
0154     unsigned long map_top;
0155     u8 byte;
0156     u16 word;
0157 
0158     /* For now I just handle the ecb2 and I assume there
0159      * are not a lot of resources up at the top of the address
0160      * space.  It is possible to handle other devices in the
0161      * top 16MiB but it is very painful.  Also since
0162      * you can only really attach a FWH to an ICHX there
0163      * a number of simplifications you can make.
0164      *
0165      * Also you can page firmware hubs if an 8MiB window isn't enough
0166      * but don't currently handle that case either.
0167      */
0168     window->pdev = pci_dev_get(pdev);
0169 
0170     /* RLG:  experiment 2.  Force the window registers to the widest values */
0171 
0172 /*
0173     pci_read_config_word(pdev, FWH_DEC_EN1, &word);
0174     printk(KERN_DEBUG "Original FWH_DEC_EN1 : %x\n", word);
0175     pci_write_config_byte(pdev, FWH_DEC_EN1, 0xff);
0176     pci_read_config_byte(pdev, FWH_DEC_EN1, &byte);
0177     printk(KERN_DEBUG "New FWH_DEC_EN1 : %x\n", byte);
0178 
0179     pci_read_config_byte(pdev, FWH_DEC_EN2, &byte);
0180     printk(KERN_DEBUG "Original FWH_DEC_EN2 : %x\n", byte);
0181     pci_write_config_byte(pdev, FWH_DEC_EN2, 0x0f);
0182     pci_read_config_byte(pdev, FWH_DEC_EN2, &byte);
0183     printk(KERN_DEBUG "New FWH_DEC_EN2 : %x\n", byte);
0184 */
0185 
0186     /* Find a region continuous to the end of the ROM window  */
0187     window->phys = 0;
0188     pci_read_config_word(pdev, FWH_DEC_EN1, &word);
0189     printk(KERN_DEBUG "pci_read_config_word : %x\n", word);
0190 
0191     if ((word & FWH_8MiB) == FWH_8MiB)
0192         window->phys = 0xff400000;
0193     else if ((word & FWH_7MiB) == FWH_7MiB)
0194         window->phys = 0xff500000;
0195     else if ((word & FWH_6MiB) == FWH_6MiB)
0196         window->phys = 0xff600000;
0197     else if ((word & FWH_5MiB) == FWH_5MiB)
0198         window->phys = 0xFF700000;
0199     else if ((word & FWH_4MiB) == FWH_4MiB)
0200         window->phys = 0xffc00000;
0201     else if ((word & FWH_3_5MiB) == FWH_3_5MiB)
0202         window->phys = 0xffc80000;
0203     else if ((word & FWH_3MiB) == FWH_3MiB)
0204         window->phys = 0xffd00000;
0205     else if ((word & FWH_2_5MiB) == FWH_2_5MiB)
0206         window->phys = 0xffd80000;
0207     else if ((word & FWH_2MiB) == FWH_2MiB)
0208         window->phys = 0xffe00000;
0209     else if ((word & FWH_1_5MiB) == FWH_1_5MiB)
0210         window->phys = 0xffe80000;
0211     else if ((word & FWH_1MiB) == FWH_1MiB)
0212         window->phys = 0xfff00000;
0213     else if ((word & FWH_0_5MiB) == FWH_0_5MiB)
0214         window->phys = 0xfff80000;
0215 
0216     if (window->phys == 0) {
0217         printk(KERN_ERR MOD_NAME ": Rom window is closed\n");
0218         goto out;
0219     }
0220 
0221     /* reserved  0x0020 and 0x0010 */
0222     window->phys -= 0x400000UL;
0223     window->size = (0xffffffffUL - window->phys) + 1UL;
0224 
0225     /* Enable writes through the rom window */
0226     pci_read_config_byte(pdev, BIOS_CNTL, &byte);
0227     if (!(byte & BIOS_WRITE_ENABLE)  && (byte & (BIOS_LOCK_ENABLE))) {
0228         /* The BIOS will generate an error if I enable
0229          * this device, so don't even try.
0230          */
0231         printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n");
0232         goto out;
0233     }
0234     pci_write_config_byte(pdev, BIOS_CNTL, byte | BIOS_WRITE_ENABLE);
0235 
0236     /*
0237      * Try to reserve the window mem region.  If this fails then
0238      * it is likely due to the window being "reserved" by the BIOS.
0239      */
0240     window->rsrc.name = MOD_NAME;
0241     window->rsrc.start = window->phys;
0242     window->rsrc.end   = window->phys + window->size - 1;
0243     window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
0244     if (request_resource(&iomem_resource, &window->rsrc)) {
0245         window->rsrc.parent = NULL;
0246         printk(KERN_DEBUG MOD_NAME ": "
0247                "%s(): Unable to register resource %pR - kernel bug?\n",
0248             __func__, &window->rsrc);
0249     }
0250 
0251     /* Map the firmware hub into my address space. */
0252     window->virt = ioremap(window->phys, window->size);
0253     if (!window->virt) {
0254         printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",
0255             window->phys, window->size);
0256         goto out;
0257     }
0258 
0259     /* Get the first address to look for an rom chip at */
0260     map_top = window->phys;
0261     if ((window->phys & 0x3fffff) != 0) {
0262         /* if not aligned on 4MiB, look 4MiB lower in address space */
0263         map_top = window->phys + 0x400000;
0264     }
0265 #if 1
0266     /* The probe sequence run over the firmware hub lock
0267      * registers sets them to 0x7 (no access).
0268      * (Insane hardware design, but most copied Intel's.)
0269      * ==> Probe at most the last 4M of the address space.
0270      */
0271     if (map_top < 0xffc00000)
0272         map_top = 0xffc00000;
0273 #endif
0274     /* Loop through and look for rom chips */
0275     while ((map_top - 1) < 0xffffffffUL) {
0276         struct cfi_private *cfi;
0277         unsigned long offset;
0278         int i;
0279 
0280         if (!map) {
0281             map = kmalloc(sizeof(*map), GFP_KERNEL);
0282             if (!map)
0283                 goto out;
0284         }
0285         memset(map, 0, sizeof(*map));
0286         INIT_LIST_HEAD(&map->list);
0287         map->map.name = map->map_name;
0288         map->map.phys = map_top;
0289         offset = map_top - window->phys;
0290         map->map.virt = (void __iomem *)
0291             (((unsigned long)(window->virt)) + offset);
0292         map->map.size = 0xffffffffUL - map_top + 1UL;
0293         /* Set the name of the map to the address I am trying */
0294         sprintf(map->map_name, "%s @%08Lx",
0295             MOD_NAME, (unsigned long long)map->map.phys);
0296 
0297         /* Firmware hubs only use vpp when being programmed
0298          * in a factory setting.  So in-place programming
0299          * needs to use a different method.
0300          */
0301         for(map->map.bankwidth = 32; map->map.bankwidth;
0302             map->map.bankwidth >>= 1) {
0303             char **probe_type;
0304             /* Skip bankwidths that are not supported */
0305             if (!map_bankwidth_supported(map->map.bankwidth))
0306                 continue;
0307 
0308             /* Setup the map methods */
0309             simple_map_init(&map->map);
0310 
0311             /* Try all of the probe methods */
0312             probe_type = rom_probe_types;
0313             for(; *probe_type; probe_type++) {
0314                 map->mtd = do_map_probe(*probe_type, &map->map);
0315                 if (map->mtd)
0316                     goto found;
0317             }
0318         }
0319         map_top += ROM_PROBE_STEP_SIZE;
0320         continue;
0321     found:
0322         /* Trim the size if we are larger than the map */
0323         if (map->mtd->size > map->map.size) {
0324             printk(KERN_WARNING MOD_NAME
0325                 " rom(%llu) larger than window(%lu). fixing...\n",
0326                 (unsigned long long)map->mtd->size, map->map.size);
0327             map->mtd->size = map->map.size;
0328         }
0329         if (window->rsrc.parent) {
0330             /*
0331              * Registering the MTD device in iomem may not be possible
0332              * if there is a BIOS "reserved" and BUSY range.  If this
0333              * fails then continue anyway.
0334              */
0335             map->rsrc.name  = map->map_name;
0336             map->rsrc.start = map->map.phys;
0337             map->rsrc.end   = map->map.phys + map->mtd->size - 1;
0338             map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
0339             if (request_resource(&window->rsrc, &map->rsrc)) {
0340                 printk(KERN_ERR MOD_NAME
0341                     ": cannot reserve MTD resource\n");
0342                 map->rsrc.parent = NULL;
0343             }
0344         }
0345 
0346         /* Make the whole region visible in the map */
0347         map->map.virt = window->virt;
0348         map->map.phys = window->phys;
0349         cfi = map->map.fldrv_priv;
0350         for(i = 0; i < cfi->numchips; i++)
0351             cfi->chips[i].start += offset;
0352 
0353         /* Now that the mtd devices is complete claim and export it */
0354         map->mtd->owner = THIS_MODULE;
0355         if (mtd_device_register(map->mtd, NULL, 0)) {
0356             map_destroy(map->mtd);
0357             map->mtd = NULL;
0358             goto out;
0359         }
0360 
0361         /* Calculate the new value of map_top */
0362         map_top += map->mtd->size;
0363 
0364         /* File away the map structure */
0365         list_add(&map->list, &window->maps);
0366         map = NULL;
0367     }
0368 
0369  out:
0370     /* Free any left over map structures */
0371     kfree(map);
0372 
0373     /* See if I have any map structures */
0374     if (list_empty(&window->maps)) {
0375         esb2rom_cleanup(window);
0376         return -ENODEV;
0377     }
0378     return 0;
0379 }
0380 
0381 static void esb2rom_remove_one(struct pci_dev *pdev)
0382 {
0383     struct esb2rom_window *window = &esb2rom_window;
0384     esb2rom_cleanup(window);
0385 }
0386 
0387 static const struct pci_device_id esb2rom_pci_tbl[] = {
0388     { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,
0389       PCI_ANY_ID, PCI_ANY_ID, },
0390     { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,
0391       PCI_ANY_ID, PCI_ANY_ID, },
0392     { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
0393       PCI_ANY_ID, PCI_ANY_ID, },
0394     { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,
0395       PCI_ANY_ID, PCI_ANY_ID, },
0396     { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1,
0397       PCI_ANY_ID, PCI_ANY_ID, },
0398     { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0,
0399       PCI_ANY_ID, PCI_ANY_ID, },
0400     { 0, },
0401 };
0402 
0403 #if 0
0404 MODULE_DEVICE_TABLE(pci, esb2rom_pci_tbl);
0405 
0406 static struct pci_driver esb2rom_driver = {
0407     .name =     MOD_NAME,
0408     .id_table = esb2rom_pci_tbl,
0409     .probe =    esb2rom_init_one,
0410     .remove =   esb2rom_remove_one,
0411 };
0412 #endif
0413 
0414 static int __init init_esb2rom(void)
0415 {
0416     struct pci_dev *pdev;
0417     const struct pci_device_id *id;
0418     int retVal;
0419 
0420     pdev = NULL;
0421     for (id = esb2rom_pci_tbl; id->vendor; id++) {
0422         printk(KERN_DEBUG "device id = %x\n", id->device);
0423         pdev = pci_get_device(id->vendor, id->device, NULL);
0424         if (pdev) {
0425             printk(KERN_DEBUG "matched device = %x\n", id->device);
0426             break;
0427         }
0428     }
0429     if (pdev) {
0430         printk(KERN_DEBUG "matched device id %x\n", id->device);
0431         retVal = esb2rom_init_one(pdev, &esb2rom_pci_tbl[0]);
0432         pci_dev_put(pdev);
0433         printk(KERN_DEBUG "retVal = %d\n", retVal);
0434         return retVal;
0435     }
0436     return -ENXIO;
0437 #if 0
0438     return pci_register_driver(&esb2rom_driver);
0439 #endif
0440 }
0441 
0442 static void __exit cleanup_esb2rom(void)
0443 {
0444     esb2rom_remove_one(esb2rom_window.pdev);
0445 }
0446 
0447 module_init(init_esb2rom);
0448 module_exit(cleanup_esb2rom);
0449 
0450 MODULE_LICENSE("GPL");
0451 MODULE_AUTHOR("Lew Glendenning <lglendenning@lnxi.com>");
0452 MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ESB2 southbridge");