162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* linux/drivers/mtd/maps/scx200_docflash.c 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci National Semiconductor SCx200 flash mapped with DOCCS 762306a36Sopenharmony_ci*/ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/module.h> 1062306a36Sopenharmony_ci#include <linux/types.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/init.h> 1362306a36Sopenharmony_ci#include <asm/io.h> 1462306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 1562306a36Sopenharmony_ci#include <linux/mtd/map.h> 1662306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci#include <linux/scx200.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#define NAME "scx200_docflash" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciMODULE_AUTHOR("Christer Weinigel <wingel@hack.org>"); 2462306a36Sopenharmony_ciMODULE_DESCRIPTION("NatSemi SCx200 DOCCS Flash Driver"); 2562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int probe = 0; /* Don't autoprobe */ 2862306a36Sopenharmony_cistatic unsigned size = 0x1000000; /* 16 MiB the whole ISA address space */ 2962306a36Sopenharmony_cistatic unsigned width = 8; /* Default to 8 bits wide */ 3062306a36Sopenharmony_cistatic char *flashtype = "cfi_probe"; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cimodule_param(probe, int, 0); 3362306a36Sopenharmony_ciMODULE_PARM_DESC(probe, "Probe for a BIOS mapping"); 3462306a36Sopenharmony_cimodule_param(size, int, 0); 3562306a36Sopenharmony_ciMODULE_PARM_DESC(size, "Size of the flash mapping"); 3662306a36Sopenharmony_cimodule_param(width, int, 0); 3762306a36Sopenharmony_ciMODULE_PARM_DESC(width, "Data width of the flash mapping (8/16)"); 3862306a36Sopenharmony_cimodule_param(flashtype, charp, 0); 3962306a36Sopenharmony_ciMODULE_PARM_DESC(flashtype, "Type of MTD probe to do"); 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_cistatic struct resource docmem = { 4262306a36Sopenharmony_ci .flags = IORESOURCE_MEM, 4362306a36Sopenharmony_ci .name = "NatSemi SCx200 DOCCS Flash", 4462306a36Sopenharmony_ci}; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic struct mtd_info *mymtd; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic struct mtd_partition partition_info[] = { 4962306a36Sopenharmony_ci { 5062306a36Sopenharmony_ci .name = "DOCCS Boot kernel", 5162306a36Sopenharmony_ci .offset = 0, 5262306a36Sopenharmony_ci .size = 0xc0000 5362306a36Sopenharmony_ci }, 5462306a36Sopenharmony_ci { 5562306a36Sopenharmony_ci .name = "DOCCS Low BIOS", 5662306a36Sopenharmony_ci .offset = 0xc0000, 5762306a36Sopenharmony_ci .size = 0x40000 5862306a36Sopenharmony_ci }, 5962306a36Sopenharmony_ci { 6062306a36Sopenharmony_ci .name = "DOCCS File system", 6162306a36Sopenharmony_ci .offset = 0x100000, 6262306a36Sopenharmony_ci .size = ~0 /* calculate from flash size */ 6362306a36Sopenharmony_ci }, 6462306a36Sopenharmony_ci { 6562306a36Sopenharmony_ci .name = "DOCCS High BIOS", 6662306a36Sopenharmony_ci .offset = ~0, /* calculate from flash size */ 6762306a36Sopenharmony_ci .size = 0x80000 6862306a36Sopenharmony_ci }, 6962306a36Sopenharmony_ci}; 7062306a36Sopenharmony_ci#define NUM_PARTITIONS ARRAY_SIZE(partition_info) 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic struct map_info scx200_docflash_map = { 7362306a36Sopenharmony_ci .name = "NatSemi SCx200 DOCCS Flash", 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int __init init_scx200_docflash(void) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci unsigned u; 7962306a36Sopenharmony_ci unsigned base; 8062306a36Sopenharmony_ci unsigned ctrl; 8162306a36Sopenharmony_ci unsigned pmr; 8262306a36Sopenharmony_ci struct pci_dev *bridge; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci printk(KERN_DEBUG NAME ": NatSemi SCx200 DOCCS Flash Driver\n"); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci if ((bridge = pci_get_device(PCI_VENDOR_ID_NS, 8762306a36Sopenharmony_ci PCI_DEVICE_ID_NS_SCx200_BRIDGE, 8862306a36Sopenharmony_ci NULL)) == NULL) 8962306a36Sopenharmony_ci return -ENODEV; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* check that we have found the configuration block */ 9262306a36Sopenharmony_ci if (!scx200_cb_present()) { 9362306a36Sopenharmony_ci pci_dev_put(bridge); 9462306a36Sopenharmony_ci return -ENODEV; 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (probe) { 9862306a36Sopenharmony_ci /* Try to use the present flash mapping if any */ 9962306a36Sopenharmony_ci pci_read_config_dword(bridge, SCx200_DOCCS_BASE, &base); 10062306a36Sopenharmony_ci pci_read_config_dword(bridge, SCx200_DOCCS_CTRL, &ctrl); 10162306a36Sopenharmony_ci pci_dev_put(bridge); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci pmr = inl(scx200_cb_base + SCx200_PMR); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci if (base == 0 10662306a36Sopenharmony_ci || (ctrl & 0x07000000) != 0x07000000 10762306a36Sopenharmony_ci || (ctrl & 0x0007ffff) == 0) 10862306a36Sopenharmony_ci return -ENODEV; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci size = ((ctrl&0x1fff)<<13) + (1<<13); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci for (u = size; u > 1; u >>= 1) 11362306a36Sopenharmony_ci ; 11462306a36Sopenharmony_ci if (u != 1) 11562306a36Sopenharmony_ci return -ENODEV; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (pmr & (1<<6)) 11862306a36Sopenharmony_ci width = 16; 11962306a36Sopenharmony_ci else 12062306a36Sopenharmony_ci width = 8; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci docmem.start = base; 12362306a36Sopenharmony_ci docmem.end = base + size; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (request_resource(&iomem_resource, &docmem)) { 12662306a36Sopenharmony_ci printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n"); 12762306a36Sopenharmony_ci return -ENOMEM; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci } else { 13062306a36Sopenharmony_ci pci_dev_put(bridge); 13162306a36Sopenharmony_ci for (u = size; u > 1; u >>= 1) 13262306a36Sopenharmony_ci ; 13362306a36Sopenharmony_ci if (u != 1) { 13462306a36Sopenharmony_ci printk(KERN_ERR NAME ": invalid size for flash mapping\n"); 13562306a36Sopenharmony_ci return -EINVAL; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (width != 8 && width != 16) { 13962306a36Sopenharmony_ci printk(KERN_ERR NAME ": invalid bus width for flash mapping\n"); 14062306a36Sopenharmony_ci return -EINVAL; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (allocate_resource(&iomem_resource, &docmem, 14462306a36Sopenharmony_ci size, 14562306a36Sopenharmony_ci 0xc0000000, 0xffffffff, 14662306a36Sopenharmony_ci size, NULL, NULL)) { 14762306a36Sopenharmony_ci printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n"); 14862306a36Sopenharmony_ci return -ENOMEM; 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci ctrl = 0x07000000 | ((size-1) >> 13); 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci printk(KERN_INFO "DOCCS BASE=0x%08lx, CTRL=0x%08lx\n", (long)docmem.start, (long)ctrl); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci pci_write_config_dword(bridge, SCx200_DOCCS_BASE, docmem.start); 15662306a36Sopenharmony_ci pci_write_config_dword(bridge, SCx200_DOCCS_CTRL, ctrl); 15762306a36Sopenharmony_ci pmr = inl(scx200_cb_base + SCx200_PMR); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (width == 8) { 16062306a36Sopenharmony_ci pmr &= ~(1<<6); 16162306a36Sopenharmony_ci } else { 16262306a36Sopenharmony_ci pmr |= (1<<6); 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci outl(pmr, scx200_cb_base + SCx200_PMR); 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci printk(KERN_INFO NAME ": DOCCS mapped at %pR, width %d\n", 16862306a36Sopenharmony_ci &docmem, width); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci scx200_docflash_map.size = size; 17162306a36Sopenharmony_ci if (width == 8) 17262306a36Sopenharmony_ci scx200_docflash_map.bankwidth = 1; 17362306a36Sopenharmony_ci else 17462306a36Sopenharmony_ci scx200_docflash_map.bankwidth = 2; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci simple_map_init(&scx200_docflash_map); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci scx200_docflash_map.phys = docmem.start; 17962306a36Sopenharmony_ci scx200_docflash_map.virt = ioremap(docmem.start, scx200_docflash_map.size); 18062306a36Sopenharmony_ci if (!scx200_docflash_map.virt) { 18162306a36Sopenharmony_ci printk(KERN_ERR NAME ": failed to ioremap the flash\n"); 18262306a36Sopenharmony_ci release_resource(&docmem); 18362306a36Sopenharmony_ci return -EIO; 18462306a36Sopenharmony_ci } 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci mymtd = do_map_probe(flashtype, &scx200_docflash_map); 18762306a36Sopenharmony_ci if (!mymtd) { 18862306a36Sopenharmony_ci printk(KERN_ERR NAME ": unable to detect flash\n"); 18962306a36Sopenharmony_ci iounmap(scx200_docflash_map.virt); 19062306a36Sopenharmony_ci release_resource(&docmem); 19162306a36Sopenharmony_ci return -ENXIO; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (size < mymtd->size) 19562306a36Sopenharmony_ci printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n"); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci mymtd->owner = THIS_MODULE; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci partition_info[3].offset = mymtd->size-partition_info[3].size; 20062306a36Sopenharmony_ci partition_info[2].size = partition_info[3].offset-partition_info[2].offset; 20162306a36Sopenharmony_ci mtd_device_register(mymtd, partition_info, NUM_PARTITIONS); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci return 0; 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void __exit cleanup_scx200_docflash(void) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci if (mymtd) { 20962306a36Sopenharmony_ci mtd_device_unregister(mymtd); 21062306a36Sopenharmony_ci map_destroy(mymtd); 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci if (scx200_docflash_map.virt) { 21362306a36Sopenharmony_ci iounmap(scx200_docflash_map.virt); 21462306a36Sopenharmony_ci release_resource(&docmem); 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cimodule_init(init_scx200_docflash); 21962306a36Sopenharmony_cimodule_exit(cleanup_scx200_docflash); 220