18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* sbc_gxx.c -- MTD map driver for Arcom Control Systems SBC-MediaGX, 38c2ecf20Sopenharmony_ci SBC-GXm and SBC-GX1 series boards. 48c2ecf20Sopenharmony_ci 58c2ecf20Sopenharmony_ci Copyright (C) 2001 Arcom Control System Ltd 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ciThe SBC-MediaGX / SBC-GXx has up to 16 MiB of 98c2ecf20Sopenharmony_ciIntel StrataFlash (28F320/28F640) in x8 mode. 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ciThis driver uses the CFI probe and Intel Extended Command Set drivers. 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ciThe flash is accessed as follows: 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci 16 KiB memory window at 0xdc000-0xdffff 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci Two IO address locations for paging 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci 0x258 208c2ecf20Sopenharmony_ci bit 0-7: address bit 14-21 218c2ecf20Sopenharmony_ci 0x259 228c2ecf20Sopenharmony_ci bit 0-1: address bit 22-23 238c2ecf20Sopenharmony_ci bit 7: 0 - reset/powered down 248c2ecf20Sopenharmony_ci 1 - device enabled 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ciThe single flash device is divided into 3 partition which appear as 278c2ecf20Sopenharmony_ciseparate MTD devices. 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci25/04/2001 AJL (Arcom) Modified signon strings and partition sizes 308c2ecf20Sopenharmony_ci (to support bzImages up to 638KiB-ish) 318c2ecf20Sopenharmony_ci*/ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci// Includes 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <linux/module.h> 368c2ecf20Sopenharmony_ci#include <linux/ioport.h> 378c2ecf20Sopenharmony_ci#include <linux/init.h> 388c2ecf20Sopenharmony_ci#include <asm/io.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h> 418c2ecf20Sopenharmony_ci#include <linux/mtd/map.h> 428c2ecf20Sopenharmony_ci#include <linux/mtd/partitions.h> 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci// Defines 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci// - Hardware specific 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define WINDOW_START 0xdc000 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* Number of bits in offset. */ 518c2ecf20Sopenharmony_ci#define WINDOW_SHIFT 14 528c2ecf20Sopenharmony_ci#define WINDOW_LENGTH (1 << WINDOW_SHIFT) 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* The bits for the offset into the window. */ 558c2ecf20Sopenharmony_ci#define WINDOW_MASK (WINDOW_LENGTH-1) 568c2ecf20Sopenharmony_ci#define PAGE_IO 0x258 578c2ecf20Sopenharmony_ci#define PAGE_IO_SIZE 2 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci/* bit 7 of 0x259 must be 1 to enable device. */ 608c2ecf20Sopenharmony_ci#define DEVICE_ENABLE 0x8000 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci// - Flash / Partition sizing 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define MAX_SIZE_KiB 16384 658c2ecf20Sopenharmony_ci#define BOOT_PARTITION_SIZE_KiB 768 668c2ecf20Sopenharmony_ci#define DATA_PARTITION_SIZE_KiB 1280 678c2ecf20Sopenharmony_ci#define APP_PARTITION_SIZE_KiB 6144 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci// Globals 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic volatile int page_in_window = -1; // Current page in window. 728c2ecf20Sopenharmony_cistatic void __iomem *iomapadr; 738c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(sbc_gxx_spin); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* partition_info gives details on the logical partitions that the split the 768c2ecf20Sopenharmony_ci * single flash device into. If the size if zero we use up to the end of the 778c2ecf20Sopenharmony_ci * device. */ 788c2ecf20Sopenharmony_cistatic const struct mtd_partition partition_info[] = { 798c2ecf20Sopenharmony_ci { .name = "SBC-GXx flash boot partition", 808c2ecf20Sopenharmony_ci .offset = 0, 818c2ecf20Sopenharmony_ci .size = BOOT_PARTITION_SIZE_KiB*1024 }, 828c2ecf20Sopenharmony_ci { .name = "SBC-GXx flash data partition", 838c2ecf20Sopenharmony_ci .offset = BOOT_PARTITION_SIZE_KiB*1024, 848c2ecf20Sopenharmony_ci .size = (DATA_PARTITION_SIZE_KiB)*1024 }, 858c2ecf20Sopenharmony_ci { .name = "SBC-GXx flash application partition", 868c2ecf20Sopenharmony_ci .offset = (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 } 878c2ecf20Sopenharmony_ci}; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci#define NUM_PARTITIONS 3 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_cistatic inline void sbc_gxx_page(struct map_info *map, unsigned long ofs) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci unsigned long page = ofs >> WINDOW_SHIFT; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if( page!=page_in_window ) { 968c2ecf20Sopenharmony_ci outw( page | DEVICE_ENABLE, PAGE_IO ); 978c2ecf20Sopenharmony_ci page_in_window = page; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci} 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic map_word sbc_gxx_read8(struct map_info *map, unsigned long ofs) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci map_word ret; 1058c2ecf20Sopenharmony_ci spin_lock(&sbc_gxx_spin); 1068c2ecf20Sopenharmony_ci sbc_gxx_page(map, ofs); 1078c2ecf20Sopenharmony_ci ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK)); 1088c2ecf20Sopenharmony_ci spin_unlock(&sbc_gxx_spin); 1098c2ecf20Sopenharmony_ci return ret; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic void sbc_gxx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci while(len) { 1158c2ecf20Sopenharmony_ci unsigned long thislen = len; 1168c2ecf20Sopenharmony_ci if (len > (WINDOW_LENGTH - (from & WINDOW_MASK))) 1178c2ecf20Sopenharmony_ci thislen = WINDOW_LENGTH-(from & WINDOW_MASK); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci spin_lock(&sbc_gxx_spin); 1208c2ecf20Sopenharmony_ci sbc_gxx_page(map, from); 1218c2ecf20Sopenharmony_ci memcpy_fromio(to, iomapadr + (from & WINDOW_MASK), thislen); 1228c2ecf20Sopenharmony_ci spin_unlock(&sbc_gxx_spin); 1238c2ecf20Sopenharmony_ci to += thislen; 1248c2ecf20Sopenharmony_ci from += thislen; 1258c2ecf20Sopenharmony_ci len -= thislen; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic void sbc_gxx_write8(struct map_info *map, map_word d, unsigned long adr) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci spin_lock(&sbc_gxx_spin); 1328c2ecf20Sopenharmony_ci sbc_gxx_page(map, adr); 1338c2ecf20Sopenharmony_ci writeb(d.x[0], iomapadr + (adr & WINDOW_MASK)); 1348c2ecf20Sopenharmony_ci spin_unlock(&sbc_gxx_spin); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic void sbc_gxx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci while(len) { 1408c2ecf20Sopenharmony_ci unsigned long thislen = len; 1418c2ecf20Sopenharmony_ci if (len > (WINDOW_LENGTH - (to & WINDOW_MASK))) 1428c2ecf20Sopenharmony_ci thislen = WINDOW_LENGTH-(to & WINDOW_MASK); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci spin_lock(&sbc_gxx_spin); 1458c2ecf20Sopenharmony_ci sbc_gxx_page(map, to); 1468c2ecf20Sopenharmony_ci memcpy_toio(iomapadr + (to & WINDOW_MASK), from, thislen); 1478c2ecf20Sopenharmony_ci spin_unlock(&sbc_gxx_spin); 1488c2ecf20Sopenharmony_ci to += thislen; 1498c2ecf20Sopenharmony_ci from += thislen; 1508c2ecf20Sopenharmony_ci len -= thislen; 1518c2ecf20Sopenharmony_ci } 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic struct map_info sbc_gxx_map = { 1558c2ecf20Sopenharmony_ci .name = "SBC-GXx flash", 1568c2ecf20Sopenharmony_ci .phys = NO_XIP, 1578c2ecf20Sopenharmony_ci .size = MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount 1588c2ecf20Sopenharmony_ci of flash so the cfi probe routines find all 1598c2ecf20Sopenharmony_ci the chips */ 1608c2ecf20Sopenharmony_ci .bankwidth = 1, 1618c2ecf20Sopenharmony_ci .read = sbc_gxx_read8, 1628c2ecf20Sopenharmony_ci .copy_from = sbc_gxx_copy_from, 1638c2ecf20Sopenharmony_ci .write = sbc_gxx_write8, 1648c2ecf20Sopenharmony_ci .copy_to = sbc_gxx_copy_to 1658c2ecf20Sopenharmony_ci}; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* MTD device for all of the flash. */ 1688c2ecf20Sopenharmony_cistatic struct mtd_info *all_mtd; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic void cleanup_sbc_gxx(void) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci if( all_mtd ) { 1738c2ecf20Sopenharmony_ci mtd_device_unregister(all_mtd); 1748c2ecf20Sopenharmony_ci map_destroy( all_mtd ); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci iounmap(iomapadr); 1788c2ecf20Sopenharmony_ci release_region(PAGE_IO,PAGE_IO_SIZE); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic int __init init_sbc_gxx(void) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci iomapadr = ioremap(WINDOW_START, WINDOW_LENGTH); 1848c2ecf20Sopenharmony_ci if (!iomapadr) { 1858c2ecf20Sopenharmony_ci printk( KERN_ERR"%s: failed to ioremap memory region\n", 1868c2ecf20Sopenharmony_ci sbc_gxx_map.name ); 1878c2ecf20Sopenharmony_ci return -EIO; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci if (!request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash")) { 1918c2ecf20Sopenharmony_ci printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n", 1928c2ecf20Sopenharmony_ci sbc_gxx_map.name, 1938c2ecf20Sopenharmony_ci PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); 1948c2ecf20Sopenharmony_ci iounmap(iomapadr); 1958c2ecf20Sopenharmony_ci return -EAGAIN; 1968c2ecf20Sopenharmony_ci } 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n", 2008c2ecf20Sopenharmony_ci sbc_gxx_map.name, 2018c2ecf20Sopenharmony_ci PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1, 2028c2ecf20Sopenharmony_ci WINDOW_START, WINDOW_START+WINDOW_LENGTH-1 ); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci /* Probe for chip. */ 2058c2ecf20Sopenharmony_ci all_mtd = do_map_probe( "cfi_probe", &sbc_gxx_map ); 2068c2ecf20Sopenharmony_ci if( !all_mtd ) { 2078c2ecf20Sopenharmony_ci cleanup_sbc_gxx(); 2088c2ecf20Sopenharmony_ci return -ENXIO; 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci all_mtd->owner = THIS_MODULE; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* Create MTD devices for each partition. */ 2148c2ecf20Sopenharmony_ci mtd_device_register(all_mtd, partition_info, NUM_PARTITIONS); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cimodule_init(init_sbc_gxx); 2208c2ecf20Sopenharmony_cimodule_exit(cleanup_sbc_gxx); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 2238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Arcom Control Systems Ltd."); 2248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MTD map driver for SBC-GXm and SBC-GX1 series boards"); 225