162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* sbc_gxx.c -- MTD map driver for Arcom Control Systems SBC-MediaGX, 362306a36Sopenharmony_ci SBC-GXm and SBC-GX1 series boards. 462306a36Sopenharmony_ci 562306a36Sopenharmony_ci Copyright (C) 2001 Arcom Control System Ltd 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci 862306a36Sopenharmony_ciThe SBC-MediaGX / SBC-GXx has up to 16 MiB of 962306a36Sopenharmony_ciIntel StrataFlash (28F320/28F640) in x8 mode. 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ciThis driver uses the CFI probe and Intel Extended Command Set drivers. 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ciThe flash is accessed as follows: 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci 16 KiB memory window at 0xdc000-0xdffff 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci Two IO address locations for paging 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci 0x258 2062306a36Sopenharmony_ci bit 0-7: address bit 14-21 2162306a36Sopenharmony_ci 0x259 2262306a36Sopenharmony_ci bit 0-1: address bit 22-23 2362306a36Sopenharmony_ci bit 7: 0 - reset/powered down 2462306a36Sopenharmony_ci 1 - device enabled 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ciThe single flash device is divided into 3 partition which appear as 2762306a36Sopenharmony_ciseparate MTD devices. 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci25/04/2001 AJL (Arcom) Modified signon strings and partition sizes 3062306a36Sopenharmony_ci (to support bzImages up to 638KiB-ish) 3162306a36Sopenharmony_ci*/ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci// Includes 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <linux/module.h> 3662306a36Sopenharmony_ci#include <linux/ioport.h> 3762306a36Sopenharmony_ci#include <linux/init.h> 3862306a36Sopenharmony_ci#include <asm/io.h> 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#include <linux/mtd/mtd.h> 4162306a36Sopenharmony_ci#include <linux/mtd/map.h> 4262306a36Sopenharmony_ci#include <linux/mtd/partitions.h> 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci// Defines 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci// - Hardware specific 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define WINDOW_START 0xdc000 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* Number of bits in offset. */ 5162306a36Sopenharmony_ci#define WINDOW_SHIFT 14 5262306a36Sopenharmony_ci#define WINDOW_LENGTH (1 << WINDOW_SHIFT) 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* The bits for the offset into the window. */ 5562306a36Sopenharmony_ci#define WINDOW_MASK (WINDOW_LENGTH-1) 5662306a36Sopenharmony_ci#define PAGE_IO 0x258 5762306a36Sopenharmony_ci#define PAGE_IO_SIZE 2 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/* bit 7 of 0x259 must be 1 to enable device. */ 6062306a36Sopenharmony_ci#define DEVICE_ENABLE 0x8000 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci// - Flash / Partition sizing 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define MAX_SIZE_KiB 16384 6562306a36Sopenharmony_ci#define BOOT_PARTITION_SIZE_KiB 768 6662306a36Sopenharmony_ci#define DATA_PARTITION_SIZE_KiB 1280 6762306a36Sopenharmony_ci#define APP_PARTITION_SIZE_KiB 6144 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci// Globals 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_cistatic volatile int page_in_window = -1; // Current page in window. 7262306a36Sopenharmony_cistatic void __iomem *iomapadr; 7362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(sbc_gxx_spin); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci/* partition_info gives details on the logical partitions that the split the 7662306a36Sopenharmony_ci * single flash device into. If the size if zero we use up to the end of the 7762306a36Sopenharmony_ci * device. */ 7862306a36Sopenharmony_cistatic const struct mtd_partition partition_info[] = { 7962306a36Sopenharmony_ci { .name = "SBC-GXx flash boot partition", 8062306a36Sopenharmony_ci .offset = 0, 8162306a36Sopenharmony_ci .size = BOOT_PARTITION_SIZE_KiB*1024 }, 8262306a36Sopenharmony_ci { .name = "SBC-GXx flash data partition", 8362306a36Sopenharmony_ci .offset = BOOT_PARTITION_SIZE_KiB*1024, 8462306a36Sopenharmony_ci .size = (DATA_PARTITION_SIZE_KiB)*1024 }, 8562306a36Sopenharmony_ci { .name = "SBC-GXx flash application partition", 8662306a36Sopenharmony_ci .offset = (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 } 8762306a36Sopenharmony_ci}; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define NUM_PARTITIONS 3 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic inline void sbc_gxx_page(struct map_info *map, unsigned long ofs) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci unsigned long page = ofs >> WINDOW_SHIFT; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci if( page!=page_in_window ) { 9662306a36Sopenharmony_ci outw( page | DEVICE_ENABLE, PAGE_IO ); 9762306a36Sopenharmony_ci page_in_window = page; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistatic map_word sbc_gxx_read8(struct map_info *map, unsigned long ofs) 10362306a36Sopenharmony_ci{ 10462306a36Sopenharmony_ci map_word ret; 10562306a36Sopenharmony_ci spin_lock(&sbc_gxx_spin); 10662306a36Sopenharmony_ci sbc_gxx_page(map, ofs); 10762306a36Sopenharmony_ci ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK)); 10862306a36Sopenharmony_ci spin_unlock(&sbc_gxx_spin); 10962306a36Sopenharmony_ci return ret; 11062306a36Sopenharmony_ci} 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cistatic void sbc_gxx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) 11362306a36Sopenharmony_ci{ 11462306a36Sopenharmony_ci while(len) { 11562306a36Sopenharmony_ci unsigned long thislen = len; 11662306a36Sopenharmony_ci if (len > (WINDOW_LENGTH - (from & WINDOW_MASK))) 11762306a36Sopenharmony_ci thislen = WINDOW_LENGTH-(from & WINDOW_MASK); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci spin_lock(&sbc_gxx_spin); 12062306a36Sopenharmony_ci sbc_gxx_page(map, from); 12162306a36Sopenharmony_ci memcpy_fromio(to, iomapadr + (from & WINDOW_MASK), thislen); 12262306a36Sopenharmony_ci spin_unlock(&sbc_gxx_spin); 12362306a36Sopenharmony_ci to += thislen; 12462306a36Sopenharmony_ci from += thislen; 12562306a36Sopenharmony_ci len -= thislen; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic void sbc_gxx_write8(struct map_info *map, map_word d, unsigned long adr) 13062306a36Sopenharmony_ci{ 13162306a36Sopenharmony_ci spin_lock(&sbc_gxx_spin); 13262306a36Sopenharmony_ci sbc_gxx_page(map, adr); 13362306a36Sopenharmony_ci writeb(d.x[0], iomapadr + (adr & WINDOW_MASK)); 13462306a36Sopenharmony_ci spin_unlock(&sbc_gxx_spin); 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void sbc_gxx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) 13862306a36Sopenharmony_ci{ 13962306a36Sopenharmony_ci while(len) { 14062306a36Sopenharmony_ci unsigned long thislen = len; 14162306a36Sopenharmony_ci if (len > (WINDOW_LENGTH - (to & WINDOW_MASK))) 14262306a36Sopenharmony_ci thislen = WINDOW_LENGTH-(to & WINDOW_MASK); 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci spin_lock(&sbc_gxx_spin); 14562306a36Sopenharmony_ci sbc_gxx_page(map, to); 14662306a36Sopenharmony_ci memcpy_toio(iomapadr + (to & WINDOW_MASK), from, thislen); 14762306a36Sopenharmony_ci spin_unlock(&sbc_gxx_spin); 14862306a36Sopenharmony_ci to += thislen; 14962306a36Sopenharmony_ci from += thislen; 15062306a36Sopenharmony_ci len -= thislen; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci} 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cistatic struct map_info sbc_gxx_map = { 15562306a36Sopenharmony_ci .name = "SBC-GXx flash", 15662306a36Sopenharmony_ci .phys = NO_XIP, 15762306a36Sopenharmony_ci .size = MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount 15862306a36Sopenharmony_ci of flash so the cfi probe routines find all 15962306a36Sopenharmony_ci the chips */ 16062306a36Sopenharmony_ci .bankwidth = 1, 16162306a36Sopenharmony_ci .read = sbc_gxx_read8, 16262306a36Sopenharmony_ci .copy_from = sbc_gxx_copy_from, 16362306a36Sopenharmony_ci .write = sbc_gxx_write8, 16462306a36Sopenharmony_ci .copy_to = sbc_gxx_copy_to 16562306a36Sopenharmony_ci}; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/* MTD device for all of the flash. */ 16862306a36Sopenharmony_cistatic struct mtd_info *all_mtd; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic void cleanup_sbc_gxx(void) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci if( all_mtd ) { 17362306a36Sopenharmony_ci mtd_device_unregister(all_mtd); 17462306a36Sopenharmony_ci map_destroy( all_mtd ); 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci iounmap(iomapadr); 17862306a36Sopenharmony_ci release_region(PAGE_IO,PAGE_IO_SIZE); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int __init init_sbc_gxx(void) 18262306a36Sopenharmony_ci{ 18362306a36Sopenharmony_ci iomapadr = ioremap(WINDOW_START, WINDOW_LENGTH); 18462306a36Sopenharmony_ci if (!iomapadr) { 18562306a36Sopenharmony_ci printk( KERN_ERR"%s: failed to ioremap memory region\n", 18662306a36Sopenharmony_ci sbc_gxx_map.name ); 18762306a36Sopenharmony_ci return -EIO; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (!request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash")) { 19162306a36Sopenharmony_ci printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n", 19262306a36Sopenharmony_ci sbc_gxx_map.name, 19362306a36Sopenharmony_ci PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); 19462306a36Sopenharmony_ci iounmap(iomapadr); 19562306a36Sopenharmony_ci return -EAGAIN; 19662306a36Sopenharmony_ci } 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n", 20062306a36Sopenharmony_ci sbc_gxx_map.name, 20162306a36Sopenharmony_ci PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1, 20262306a36Sopenharmony_ci WINDOW_START, WINDOW_START+WINDOW_LENGTH-1 ); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci /* Probe for chip. */ 20562306a36Sopenharmony_ci all_mtd = do_map_probe( "cfi_probe", &sbc_gxx_map ); 20662306a36Sopenharmony_ci if( !all_mtd ) { 20762306a36Sopenharmony_ci cleanup_sbc_gxx(); 20862306a36Sopenharmony_ci return -ENXIO; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci all_mtd->owner = THIS_MODULE; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* Create MTD devices for each partition. */ 21462306a36Sopenharmony_ci mtd_device_register(all_mtd, partition_info, NUM_PARTITIONS); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return 0; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cimodule_init(init_sbc_gxx); 22062306a36Sopenharmony_cimodule_exit(cleanup_sbc_gxx); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 22362306a36Sopenharmony_ciMODULE_AUTHOR("Arcom Control Systems Ltd."); 22462306a36Sopenharmony_ciMODULE_DESCRIPTION("MTD map driver for SBC-GXm and SBC-GX1 series boards"); 225