18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* linux/drivers/mtd/maps/scx200_docflash.c 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci National Semiconductor SCx200 flash mapped with DOCCS 78c2ecf20Sopenharmony_ci*/ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <asm/io.h> 148c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 158c2ecf20Sopenharmony_ci#include <linux/mtd/map.h> 168c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/pci.h> 198c2ecf20Sopenharmony_ci#include <linux/scx200.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define NAME "scx200_docflash" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Christer Weinigel <wingel@hack.org>"); 248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("NatSemi SCx200 DOCCS Flash Driver"); 258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int probe = 0; /* Don't autoprobe */ 288c2ecf20Sopenharmony_cistatic unsigned size = 0x1000000; /* 16 MiB the whole ISA address space */ 298c2ecf20Sopenharmony_cistatic unsigned width = 8; /* Default to 8 bits wide */ 308c2ecf20Sopenharmony_cistatic char *flashtype = "cfi_probe"; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cimodule_param(probe, int, 0); 338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(probe, "Probe for a BIOS mapping"); 348c2ecf20Sopenharmony_cimodule_param(size, int, 0); 358c2ecf20Sopenharmony_ciMODULE_PARM_DESC(size, "Size of the flash mapping"); 368c2ecf20Sopenharmony_cimodule_param(width, int, 0); 378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(width, "Data width of the flash mapping (8/16)"); 388c2ecf20Sopenharmony_cimodule_param(flashtype, charp, 0); 398c2ecf20Sopenharmony_ciMODULE_PARM_DESC(flashtype, "Type of MTD probe to do"); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic struct resource docmem = { 428c2ecf20Sopenharmony_ci .flags = IORESOURCE_MEM, 438c2ecf20Sopenharmony_ci .name = "NatSemi SCx200 DOCCS Flash", 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic struct mtd_info *mymtd; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic struct mtd_partition partition_info[] = { 498c2ecf20Sopenharmony_ci { 508c2ecf20Sopenharmony_ci .name = "DOCCS Boot kernel", 518c2ecf20Sopenharmony_ci .offset = 0, 528c2ecf20Sopenharmony_ci .size = 0xc0000 538c2ecf20Sopenharmony_ci }, 548c2ecf20Sopenharmony_ci { 558c2ecf20Sopenharmony_ci .name = "DOCCS Low BIOS", 568c2ecf20Sopenharmony_ci .offset = 0xc0000, 578c2ecf20Sopenharmony_ci .size = 0x40000 588c2ecf20Sopenharmony_ci }, 598c2ecf20Sopenharmony_ci { 608c2ecf20Sopenharmony_ci .name = "DOCCS File system", 618c2ecf20Sopenharmony_ci .offset = 0x100000, 628c2ecf20Sopenharmony_ci .size = ~0 /* calculate from flash size */ 638c2ecf20Sopenharmony_ci }, 648c2ecf20Sopenharmony_ci { 658c2ecf20Sopenharmony_ci .name = "DOCCS High BIOS", 668c2ecf20Sopenharmony_ci .offset = ~0, /* calculate from flash size */ 678c2ecf20Sopenharmony_ci .size = 0x80000 688c2ecf20Sopenharmony_ci }, 698c2ecf20Sopenharmony_ci}; 708c2ecf20Sopenharmony_ci#define NUM_PARTITIONS ARRAY_SIZE(partition_info) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic struct map_info scx200_docflash_map = { 738c2ecf20Sopenharmony_ci .name = "NatSemi SCx200 DOCCS Flash", 748c2ecf20Sopenharmony_ci}; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int __init init_scx200_docflash(void) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci unsigned u; 798c2ecf20Sopenharmony_ci unsigned base; 808c2ecf20Sopenharmony_ci unsigned ctrl; 818c2ecf20Sopenharmony_ci unsigned pmr; 828c2ecf20Sopenharmony_ci struct pci_dev *bridge; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci printk(KERN_DEBUG NAME ": NatSemi SCx200 DOCCS Flash Driver\n"); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci if ((bridge = pci_get_device(PCI_VENDOR_ID_NS, 878c2ecf20Sopenharmony_ci PCI_DEVICE_ID_NS_SCx200_BRIDGE, 888c2ecf20Sopenharmony_ci NULL)) == NULL) 898c2ecf20Sopenharmony_ci return -ENODEV; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* check that we have found the configuration block */ 928c2ecf20Sopenharmony_ci if (!scx200_cb_present()) { 938c2ecf20Sopenharmony_ci pci_dev_put(bridge); 948c2ecf20Sopenharmony_ci return -ENODEV; 958c2ecf20Sopenharmony_ci } 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci if (probe) { 988c2ecf20Sopenharmony_ci /* Try to use the present flash mapping if any */ 998c2ecf20Sopenharmony_ci pci_read_config_dword(bridge, SCx200_DOCCS_BASE, &base); 1008c2ecf20Sopenharmony_ci pci_read_config_dword(bridge, SCx200_DOCCS_CTRL, &ctrl); 1018c2ecf20Sopenharmony_ci pci_dev_put(bridge); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci pmr = inl(scx200_cb_base + SCx200_PMR); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (base == 0 1068c2ecf20Sopenharmony_ci || (ctrl & 0x07000000) != 0x07000000 1078c2ecf20Sopenharmony_ci || (ctrl & 0x0007ffff) == 0) 1088c2ecf20Sopenharmony_ci return -ENODEV; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci size = ((ctrl&0x1fff)<<13) + (1<<13); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci for (u = size; u > 1; u >>= 1) 1138c2ecf20Sopenharmony_ci ; 1148c2ecf20Sopenharmony_ci if (u != 1) 1158c2ecf20Sopenharmony_ci return -ENODEV; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci if (pmr & (1<<6)) 1188c2ecf20Sopenharmony_ci width = 16; 1198c2ecf20Sopenharmony_ci else 1208c2ecf20Sopenharmony_ci width = 8; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci docmem.start = base; 1238c2ecf20Sopenharmony_ci docmem.end = base + size; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (request_resource(&iomem_resource, &docmem)) { 1268c2ecf20Sopenharmony_ci printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n"); 1278c2ecf20Sopenharmony_ci return -ENOMEM; 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci } else { 1308c2ecf20Sopenharmony_ci pci_dev_put(bridge); 1318c2ecf20Sopenharmony_ci for (u = size; u > 1; u >>= 1) 1328c2ecf20Sopenharmony_ci ; 1338c2ecf20Sopenharmony_ci if (u != 1) { 1348c2ecf20Sopenharmony_ci printk(KERN_ERR NAME ": invalid size for flash mapping\n"); 1358c2ecf20Sopenharmony_ci return -EINVAL; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci if (width != 8 && width != 16) { 1398c2ecf20Sopenharmony_ci printk(KERN_ERR NAME ": invalid bus width for flash mapping\n"); 1408c2ecf20Sopenharmony_ci return -EINVAL; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci if (allocate_resource(&iomem_resource, &docmem, 1448c2ecf20Sopenharmony_ci size, 1458c2ecf20Sopenharmony_ci 0xc0000000, 0xffffffff, 1468c2ecf20Sopenharmony_ci size, NULL, NULL)) { 1478c2ecf20Sopenharmony_ci printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n"); 1488c2ecf20Sopenharmony_ci return -ENOMEM; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci ctrl = 0x07000000 | ((size-1) >> 13); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci printk(KERN_INFO "DOCCS BASE=0x%08lx, CTRL=0x%08lx\n", (long)docmem.start, (long)ctrl); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, SCx200_DOCCS_BASE, docmem.start); 1568c2ecf20Sopenharmony_ci pci_write_config_dword(bridge, SCx200_DOCCS_CTRL, ctrl); 1578c2ecf20Sopenharmony_ci pmr = inl(scx200_cb_base + SCx200_PMR); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (width == 8) { 1608c2ecf20Sopenharmony_ci pmr &= ~(1<<6); 1618c2ecf20Sopenharmony_ci } else { 1628c2ecf20Sopenharmony_ci pmr |= (1<<6); 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci outl(pmr, scx200_cb_base + SCx200_PMR); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci printk(KERN_INFO NAME ": DOCCS mapped at %pR, width %d\n", 1688c2ecf20Sopenharmony_ci &docmem, width); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci scx200_docflash_map.size = size; 1718c2ecf20Sopenharmony_ci if (width == 8) 1728c2ecf20Sopenharmony_ci scx200_docflash_map.bankwidth = 1; 1738c2ecf20Sopenharmony_ci else 1748c2ecf20Sopenharmony_ci scx200_docflash_map.bankwidth = 2; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci simple_map_init(&scx200_docflash_map); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci scx200_docflash_map.phys = docmem.start; 1798c2ecf20Sopenharmony_ci scx200_docflash_map.virt = ioremap(docmem.start, scx200_docflash_map.size); 1808c2ecf20Sopenharmony_ci if (!scx200_docflash_map.virt) { 1818c2ecf20Sopenharmony_ci printk(KERN_ERR NAME ": failed to ioremap the flash\n"); 1828c2ecf20Sopenharmony_ci release_resource(&docmem); 1838c2ecf20Sopenharmony_ci return -EIO; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci mymtd = do_map_probe(flashtype, &scx200_docflash_map); 1878c2ecf20Sopenharmony_ci if (!mymtd) { 1888c2ecf20Sopenharmony_ci printk(KERN_ERR NAME ": unable to detect flash\n"); 1898c2ecf20Sopenharmony_ci iounmap(scx200_docflash_map.virt); 1908c2ecf20Sopenharmony_ci release_resource(&docmem); 1918c2ecf20Sopenharmony_ci return -ENXIO; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (size < mymtd->size) 1958c2ecf20Sopenharmony_ci printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n"); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci mymtd->owner = THIS_MODULE; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci partition_info[3].offset = mymtd->size-partition_info[3].size; 2008c2ecf20Sopenharmony_ci partition_info[2].size = partition_info[3].offset-partition_info[2].offset; 2018c2ecf20Sopenharmony_ci mtd_device_register(mymtd, partition_info, NUM_PARTITIONS); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci return 0; 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic void __exit cleanup_scx200_docflash(void) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci if (mymtd) { 2098c2ecf20Sopenharmony_ci mtd_device_unregister(mymtd); 2108c2ecf20Sopenharmony_ci map_destroy(mymtd); 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci if (scx200_docflash_map.virt) { 2138c2ecf20Sopenharmony_ci iounmap(scx200_docflash_map.virt); 2148c2ecf20Sopenharmony_ci release_resource(&docmem); 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cimodule_init(init_scx200_docflash); 2198c2ecf20Sopenharmony_cimodule_exit(cleanup_scx200_docflash); 220