11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * amd76xrom.c 31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Normal mappings of chips in physical memory 51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h> 81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h> 91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h> 101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h> 115a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h> 121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h> 131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/mtd.h> 141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/map.h> 151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/cfi.h> 161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mtd/flashchip.h> 171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h> 181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci_ids.h> 191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/list.h> 201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define xstr(s) str(s) 231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define str(s) #s 241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define MOD_NAME xstr(KBUILD_BASENAME) 251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ADDRESS_NAME_LEN 18 271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */ 291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct amd76xrom_window { 311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds void __iomem *virt; 321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long phys; 331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long size; 341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct list_head maps; 351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct resource rsrc; 361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct pci_dev *pdev; 371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct amd76xrom_map_info { 401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct list_head list; 411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct map_info map; 421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct mtd_info *mtd; 431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct resource rsrc; 441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; 451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 47c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson/* The 2 bits controlling the window size are often set to allow reading 48c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * the BIOS, but too small to allow writing, since the lock registers are 49c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * 4MiB lower in the address space than the data. 50c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * 51c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * This is intended to prevent flashing the bios, perhaps accidentally. 52c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * 53c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * This parameter allows the normal driver to over-ride the BIOS settings. 54c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * 55c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * The bits are 6 and 7. If both bits are set, it is a 5MiB window. 56c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * If only the 7 Bit is set, it is a 4MiB window. Otherwise, a 57c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * 64KiB window. 58c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * 59c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson */ 60c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jacksonstatic uint win_size_bits; 61c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jacksonmodule_param(win_size_bits, uint, 0); 62c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan JacksonMODULE_PARM_DESC(win_size_bits, "ROM window size bits override for 0x43 byte, normally set by BIOS."); 63c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson 641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct amd76xrom_window amd76xrom_window = { 651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .maps = LIST_HEAD_INIT(amd76xrom_window.maps), 661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void amd76xrom_cleanup(struct amd76xrom_window *window) 691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct amd76xrom_map_info *map, *scratch; 711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 byte; 721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (window->pdev) { 741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Disable writes through the rom window */ 751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pci_read_config_byte(window->pdev, 0x40, &byte); 761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pci_write_config_byte(window->pdev, 0x40, byte & ~1); 77dd8e9ed6ed544e2b924429d29cd2a6b55590109bAlan Cox pci_dev_put(window->pdev); 781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Free all of the mtd devices */ 811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list_for_each_entry_safe(map, scratch, &window->maps, list) { 821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map->rsrc.parent) { 831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds release_resource(&map->rsrc); 841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 85ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles mtd_device_unregister(map->mtd); 861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_destroy(map->mtd); 871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list_del(&map->list); 881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds kfree(map); 891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 9069f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner if (window->rsrc.parent) 911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds release_resource(&window->rsrc); 921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (window->virt) { 941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds iounmap(window->virt); 951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->virt = NULL; 961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->phys = 0; 971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->size = 0; 981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->pdev = NULL; 991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1037bf350b7245b40fe6a57b79ec4a04c85195dc648Artem Bityutskiystatic int amd76xrom_init_one(struct pci_dev *pdev, 1047bf350b7245b40fe6a57b79ec4a04c85195dc648Artem Bityutskiy const struct pci_device_id *ent) 1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; 1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds u8 byte; 1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct amd76xrom_window *window = &amd76xrom_window; 1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct amd76xrom_map_info *map = NULL; 1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long map_top; 1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 112dd8e9ed6ed544e2b924429d29cd2a6b55590109bAlan Cox /* Remember the pci dev I find the window in - already have a ref */ 1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->pdev = pdev; 1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 115c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson /* Enable the selected rom window. This is often incorrectly 116c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * set up by the BIOS, and the 4MiB offset for the lock registers 117c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * requires the full 5MiB of window space. 118c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * 119c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * This 'write, then read' approach leaves the bits for 120c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson * other uses of the hardware info. 121c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson */ 122c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson pci_read_config_byte(pdev, 0x43, &byte); 123c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson pci_write_config_byte(pdev, 0x43, byte | win_size_bits ); 124c9073ce02adfa273a3d6d53eac8c4c035510ad9cRyan Jackson 1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Assume the rom window is properly setup, and find it's size */ 1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pci_read_config_byte(pdev, 0x43, &byte); 1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) { 1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->phys = 0xffb00000; /* 5MiB */ 1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else if ((byte & (1<<7)) == (1<<7)) { 1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->phys = 0xffc00000; /* 4MiB */ 1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds else { 1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->phys = 0xffff0000; /* 64KiB */ 1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->size = 0xffffffffUL - window->phys + 1UL; 13769f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner 1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Try to reserve the window mem region. If this fails then 1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * it is likely due to a fragment of the window being 1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * "reseved" by the BIOS. In the case that the 1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * request_mem_region() fails then once the rom size is 1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * discovered we will try to reserve the unreserved fragment. 1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->rsrc.name = MOD_NAME; 1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->rsrc.start = window->phys; 1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->rsrc.end = window->phys + window->size - 1; 1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; 1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (request_resource(&iomem_resource, &window->rsrc)) { 1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->rsrc.parent = NULL; 1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR MOD_NAME 152f9a5279c70af10e967872e922b91310a91f87b05Joe Perches " %s(): Unable to register resource %pR - kernel bug?\n", 153f9a5279c70af10e967872e922b91310a91f87b05Joe Perches __func__, &window->rsrc); 15482013d988fc03a1b908b2b0360a1e34f6152fda6Stanislaw Gruszka return -EBUSY; 1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Enable writes through the rom window */ 1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pci_read_config_byte(pdev, 0x40, &byte); 1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pci_write_config_byte(pdev, 0x40, byte | 1); 16169f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner 1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* FIXME handle registers 0x80 - 0x8C the bios region locks */ 1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* For write accesses caches are useless */ 1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->virt = ioremap_nocache(window->phys, window->size); 1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!window->virt) { 1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n", 1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds window->phys, window->size); 1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Get the first address to look for an rom chip at */ 1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_top = window->phys; 1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 1 1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* The probe sequence run over the firmware hub lock 1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * registers sets them to 0x7 (no access). 1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Probe at most the last 4M of the address space. 1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map_top < 0xffc00000) { 1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_top = 0xffc00000; 1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Loop through and look for rom chips */ 1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds while((map_top - 1) < 0xffffffffUL) { 1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct cfi_private *cfi; 1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds unsigned long offset; 1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds int i; 1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!map) { 1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map = kmalloc(sizeof(*map), GFP_KERNEL); 1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!map) { 1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR MOD_NAME ": kmalloc failed"); 1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds memset(map, 0, sizeof(*map)); 1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds INIT_LIST_HEAD(&map->list); 1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->map.name = map->map_name; 1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->map.phys = map_top; 2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds offset = map_top - window->phys; 2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->map.virt = (void __iomem *) 2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds (((unsigned long)(window->virt)) + offset); 2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->map.size = 0xffffffffUL - map_top + 1UL; 2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Set the name of the map to the address I am trying */ 2051a6284cb6f44b2484505ee7567831316a90bc09fAndrew Morton sprintf(map->map_name, "%s @%08Lx", 2061a6284cb6f44b2484505ee7567831316a90bc09fAndrew Morton MOD_NAME, (unsigned long long)map->map.phys); 2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* There is no generic VPP support */ 20969f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner for(map->map.bankwidth = 32; map->map.bankwidth; 2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->map.bankwidth >>= 1) 2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { 2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds char **probe_type; 2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Skip bankwidths that are not supported */ 2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (!map_bankwidth_supported(map->map.bankwidth)) 2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds continue; 2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Setup the map methods */ 2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds simple_map_init(&map->map); 2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Try all of the probe methods */ 2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds probe_type = rom_probe_types; 2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for(; *probe_type; probe_type++) { 2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->mtd = do_map_probe(*probe_type, &map->map); 2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map->mtd) 2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto found; 2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_top += ROM_PROBE_STEP_SIZE; 2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds continue; 2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds found: 2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Trim the size if we are larger than the map */ 2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (map->mtd->size > map->map.size) { 2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_WARNING MOD_NAME 23469423d99fc182a81f3c5db3eb5c140acc6fc64beAdrian Hunter " rom(%llu) larger than window(%lu). fixing...\n", 23569423d99fc182a81f3c5db3eb5c140acc6fc64beAdrian Hunter (unsigned long long)map->mtd->size, map->map.size); 2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->mtd->size = map->map.size; 2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (window->rsrc.parent) { 2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* 2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Registering the MTD device in iomem may not be possible 2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * if there is a BIOS "reserved" and BUSY range. If this 2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * fails then continue anyway. 2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */ 2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->rsrc.name = map->map_name; 2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->rsrc.start = map->map.phys; 2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->rsrc.end = map->map.phys + map->mtd->size - 1; 2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; 2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (request_resource(&window->rsrc, &map->rsrc)) { 2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds printk(KERN_ERR MOD_NAME 2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds ": cannot reserve MTD resource\n"); 2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->rsrc.parent = NULL; 2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Make the whole region visible in the map */ 2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->map.virt = window->virt; 2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->map.phys = window->phys; 2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi = map->map.fldrv_priv; 2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for(i = 0; i < cfi->numchips; i++) { 2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds cfi->chips[i].start += offset; 2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 26269f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner 2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Now that the mtd devices is complete claim and export it */ 2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->mtd->owner = THIS_MODULE; 265ee0e87b174bb41f0310cf089262bf5dd8f95a212Jamie Iles if (mtd_device_register(map->mtd, NULL, 0)) { 2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_destroy(map->mtd); 2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map->mtd = NULL; 2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds goto out; 2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Calculate the new value of map_top */ 2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map_top += map->mtd->size; 2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* File away the map structure */ 2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds list_add(&map->list, &window->maps); 2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds map = NULL; 2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds out: 2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* Free any left over map structures */ 282fa671646f61182cd18234461a6e65f50c6558695Jesper Juhl kfree(map); 2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds /* See if I have any map structures */ 2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (list_empty(&window->maps)) { 2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds amd76xrom_cleanup(window); 2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENODEV; 2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return 0; 2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2927bf350b7245b40fe6a57b79ec4a04c85195dc648Artem Bityutskiystatic void amd76xrom_remove_one(struct pci_dev *pdev) 2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct amd76xrom_window *window = &amd76xrom_window; 2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds amd76xrom_cleanup(window); 2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pci_device_id amd76xrom_pci_tbl[] = { 30069f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, 3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds PCI_ANY_ID, PCI_ANY_ID, }, 30269f34c98c1416eb74c55e38a21dbf3e294966514Thomas Gleixner { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7440, 3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds PCI_ANY_ID, PCI_ANY_ID, }, 3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { PCI_VENDOR_ID_AMD, 0x7468 }, /* amd8111 support */ 3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds { 0, } 3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE(pci, amd76xrom_pci_tbl); 3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0 3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pci_driver amd76xrom_driver = { 3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .name = MOD_NAME, 3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .id_table = amd76xrom_pci_tbl, 3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .probe = amd76xrom_init_one, 3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds .remove = amd76xrom_remove_one, 3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}; 3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init init_amd76xrom(void) 3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct pci_dev *pdev; 3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds struct pci_device_id *id; 3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds pdev = NULL; 3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds for(id = amd76xrom_pci_tbl; id->vendor; id++) { 325dd8e9ed6ed544e2b924429d29cd2a6b55590109bAlan Cox pdev = pci_get_device(id->vendor, id->device, NULL); 3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (pdev) { 3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds break; 3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds if (pdev) { 3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return amd76xrom_init_one(pdev, &amd76xrom_pci_tbl[0]); 3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds } 3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds return -ENXIO; 3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0 335ff3bc4eb94ec3d2ce6e8f615d38c94151ccb6553Domen Puncer return pci_register_driver(&amd76xrom_driver); 3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif 3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit cleanup_amd76xrom(void) 3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{ 3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds amd76xrom_remove_one(amd76xrom_window.pdev); 3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds} 3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(init_amd76xrom); 3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(cleanup_amd76xrom); 3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds 3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL"); 3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>"); 3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION("MTD map driver for BIOS chips on the AMD76X southbridge"); 350