1*1a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /* sbc_gxx.c -- MTD map driver for Arcom Control Systems SBC-MediaGX,
31da177e4SLinus Torvalds SBC-GXm and SBC-GX1 series boards.
41da177e4SLinus Torvalds
51da177e4SLinus Torvalds Copyright (C) 2001 Arcom Control System Ltd
61da177e4SLinus Torvalds
71da177e4SLinus Torvalds
81da177e4SLinus Torvalds The SBC-MediaGX / SBC-GXx has up to 16 MiB of
91da177e4SLinus Torvalds Intel StrataFlash (28F320/28F640) in x8 mode.
101da177e4SLinus Torvalds
111da177e4SLinus Torvalds This driver uses the CFI probe and Intel Extended Command Set drivers.
121da177e4SLinus Torvalds
131da177e4SLinus Torvalds The flash is accessed as follows:
141da177e4SLinus Torvalds
151da177e4SLinus Torvalds 16 KiB memory window at 0xdc000-0xdffff
161da177e4SLinus Torvalds
171da177e4SLinus Torvalds Two IO address locations for paging
181da177e4SLinus Torvalds
191da177e4SLinus Torvalds 0x258
201da177e4SLinus Torvalds bit 0-7: address bit 14-21
211da177e4SLinus Torvalds 0x259
221da177e4SLinus Torvalds bit 0-1: address bit 22-23
231da177e4SLinus Torvalds bit 7: 0 - reset/powered down
241da177e4SLinus Torvalds 1 - device enabled
251da177e4SLinus Torvalds
261da177e4SLinus Torvalds The single flash device is divided into 3 partition which appear as
271da177e4SLinus Torvalds separate MTD devices.
281da177e4SLinus Torvalds
291da177e4SLinus Torvalds 25/04/2001 AJL (Arcom) Modified signon strings and partition sizes
301da177e4SLinus Torvalds (to support bzImages up to 638KiB-ish)
311da177e4SLinus Torvalds */
321da177e4SLinus Torvalds
331da177e4SLinus Torvalds // Includes
341da177e4SLinus Torvalds
351da177e4SLinus Torvalds #include <linux/module.h>
361da177e4SLinus Torvalds #include <linux/ioport.h>
371da177e4SLinus Torvalds #include <linux/init.h>
381da177e4SLinus Torvalds #include <asm/io.h>
391da177e4SLinus Torvalds
401da177e4SLinus Torvalds #include <linux/mtd/mtd.h>
411da177e4SLinus Torvalds #include <linux/mtd/map.h>
421da177e4SLinus Torvalds #include <linux/mtd/partitions.h>
431da177e4SLinus Torvalds
441da177e4SLinus Torvalds // Defines
451da177e4SLinus Torvalds
461da177e4SLinus Torvalds // - Hardware specific
471da177e4SLinus Torvalds
481da177e4SLinus Torvalds #define WINDOW_START 0xdc000
491da177e4SLinus Torvalds
501da177e4SLinus Torvalds /* Number of bits in offset. */
511da177e4SLinus Torvalds #define WINDOW_SHIFT 14
521da177e4SLinus Torvalds #define WINDOW_LENGTH (1 << WINDOW_SHIFT)
531da177e4SLinus Torvalds
541da177e4SLinus Torvalds /* The bits for the offset into the window. */
551da177e4SLinus Torvalds #define WINDOW_MASK (WINDOW_LENGTH-1)
561da177e4SLinus Torvalds #define PAGE_IO 0x258
571da177e4SLinus Torvalds #define PAGE_IO_SIZE 2
581da177e4SLinus Torvalds
591da177e4SLinus Torvalds /* bit 7 of 0x259 must be 1 to enable device. */
601da177e4SLinus Torvalds #define DEVICE_ENABLE 0x8000
611da177e4SLinus Torvalds
621da177e4SLinus Torvalds // - Flash / Partition sizing
631da177e4SLinus Torvalds
641da177e4SLinus Torvalds #define MAX_SIZE_KiB 16384
651da177e4SLinus Torvalds #define BOOT_PARTITION_SIZE_KiB 768
661da177e4SLinus Torvalds #define DATA_PARTITION_SIZE_KiB 1280
671da177e4SLinus Torvalds #define APP_PARTITION_SIZE_KiB 6144
681da177e4SLinus Torvalds
691da177e4SLinus Torvalds // Globals
701da177e4SLinus Torvalds
711da177e4SLinus Torvalds static volatile int page_in_window = -1; // Current page in window.
721da177e4SLinus Torvalds static void __iomem *iomapadr;
731da177e4SLinus Torvalds static DEFINE_SPINLOCK(sbc_gxx_spin);
741da177e4SLinus Torvalds
751da177e4SLinus Torvalds /* partition_info gives details on the logical partitions that the split the
761da177e4SLinus Torvalds * single flash device into. If the size if zero we use up to the end of the
771da177e4SLinus Torvalds * device. */
78d4906688SArvind Yadav static const struct mtd_partition partition_info[] = {
791da177e4SLinus Torvalds { .name = "SBC-GXx flash boot partition",
801da177e4SLinus Torvalds .offset = 0,
811da177e4SLinus Torvalds .size = BOOT_PARTITION_SIZE_KiB*1024 },
821da177e4SLinus Torvalds { .name = "SBC-GXx flash data partition",
831da177e4SLinus Torvalds .offset = BOOT_PARTITION_SIZE_KiB*1024,
841da177e4SLinus Torvalds .size = (DATA_PARTITION_SIZE_KiB)*1024 },
851da177e4SLinus Torvalds { .name = "SBC-GXx flash application partition",
861da177e4SLinus Torvalds .offset = (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 }
871da177e4SLinus Torvalds };
881da177e4SLinus Torvalds
891da177e4SLinus Torvalds #define NUM_PARTITIONS 3
901da177e4SLinus Torvalds
sbc_gxx_page(struct map_info * map,unsigned long ofs)911da177e4SLinus Torvalds static inline void sbc_gxx_page(struct map_info *map, unsigned long ofs)
921da177e4SLinus Torvalds {
931da177e4SLinus Torvalds unsigned long page = ofs >> WINDOW_SHIFT;
941da177e4SLinus Torvalds
951da177e4SLinus Torvalds if( page!=page_in_window ) {
961da177e4SLinus Torvalds outw( page | DEVICE_ENABLE, PAGE_IO );
971da177e4SLinus Torvalds page_in_window = page;
981da177e4SLinus Torvalds }
991da177e4SLinus Torvalds }
1001da177e4SLinus Torvalds
1011da177e4SLinus Torvalds
sbc_gxx_read8(struct map_info * map,unsigned long ofs)1021da177e4SLinus Torvalds static map_word sbc_gxx_read8(struct map_info *map, unsigned long ofs)
1031da177e4SLinus Torvalds {
1041da177e4SLinus Torvalds map_word ret;
1051da177e4SLinus Torvalds spin_lock(&sbc_gxx_spin);
1061da177e4SLinus Torvalds sbc_gxx_page(map, ofs);
1071da177e4SLinus Torvalds ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK));
1081da177e4SLinus Torvalds spin_unlock(&sbc_gxx_spin);
1091da177e4SLinus Torvalds return ret;
1101da177e4SLinus Torvalds }
1111da177e4SLinus Torvalds
sbc_gxx_copy_from(struct map_info * map,void * to,unsigned long from,ssize_t len)1121da177e4SLinus Torvalds static void sbc_gxx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
1131da177e4SLinus Torvalds {
1141da177e4SLinus Torvalds while(len) {
1151da177e4SLinus Torvalds unsigned long thislen = len;
1161da177e4SLinus Torvalds if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
1171da177e4SLinus Torvalds thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
1181da177e4SLinus Torvalds
1191da177e4SLinus Torvalds spin_lock(&sbc_gxx_spin);
1201da177e4SLinus Torvalds sbc_gxx_page(map, from);
1211da177e4SLinus Torvalds memcpy_fromio(to, iomapadr + (from & WINDOW_MASK), thislen);
1221da177e4SLinus Torvalds spin_unlock(&sbc_gxx_spin);
1231da177e4SLinus Torvalds to += thislen;
1241da177e4SLinus Torvalds from += thislen;
1251da177e4SLinus Torvalds len -= thislen;
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds }
1281da177e4SLinus Torvalds
sbc_gxx_write8(struct map_info * map,map_word d,unsigned long adr)1291da177e4SLinus Torvalds static void sbc_gxx_write8(struct map_info *map, map_word d, unsigned long adr)
1301da177e4SLinus Torvalds {
1311da177e4SLinus Torvalds spin_lock(&sbc_gxx_spin);
1321da177e4SLinus Torvalds sbc_gxx_page(map, adr);
1331da177e4SLinus Torvalds writeb(d.x[0], iomapadr + (adr & WINDOW_MASK));
1341da177e4SLinus Torvalds spin_unlock(&sbc_gxx_spin);
1351da177e4SLinus Torvalds }
1361da177e4SLinus Torvalds
sbc_gxx_copy_to(struct map_info * map,unsigned long to,const void * from,ssize_t len)1371da177e4SLinus Torvalds static void sbc_gxx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
1381da177e4SLinus Torvalds {
1391da177e4SLinus Torvalds while(len) {
1401da177e4SLinus Torvalds unsigned long thislen = len;
1411da177e4SLinus Torvalds if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
1421da177e4SLinus Torvalds thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
1431da177e4SLinus Torvalds
1441da177e4SLinus Torvalds spin_lock(&sbc_gxx_spin);
1451da177e4SLinus Torvalds sbc_gxx_page(map, to);
1461da177e4SLinus Torvalds memcpy_toio(iomapadr + (to & WINDOW_MASK), from, thislen);
1471da177e4SLinus Torvalds spin_unlock(&sbc_gxx_spin);
1481da177e4SLinus Torvalds to += thislen;
1491da177e4SLinus Torvalds from += thislen;
1501da177e4SLinus Torvalds len -= thislen;
1511da177e4SLinus Torvalds }
1521da177e4SLinus Torvalds }
1531da177e4SLinus Torvalds
1541da177e4SLinus Torvalds static struct map_info sbc_gxx_map = {
1551da177e4SLinus Torvalds .name = "SBC-GXx flash",
1561da177e4SLinus Torvalds .phys = NO_XIP,
1571da177e4SLinus Torvalds .size = MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount
1581da177e4SLinus Torvalds of flash so the cfi probe routines find all
1591da177e4SLinus Torvalds the chips */
1601da177e4SLinus Torvalds .bankwidth = 1,
1611da177e4SLinus Torvalds .read = sbc_gxx_read8,
1621da177e4SLinus Torvalds .copy_from = sbc_gxx_copy_from,
1631da177e4SLinus Torvalds .write = sbc_gxx_write8,
1641da177e4SLinus Torvalds .copy_to = sbc_gxx_copy_to
1651da177e4SLinus Torvalds };
1661da177e4SLinus Torvalds
1671da177e4SLinus Torvalds /* MTD device for all of the flash. */
1681da177e4SLinus Torvalds static struct mtd_info *all_mtd;
1691da177e4SLinus Torvalds
cleanup_sbc_gxx(void)1701da177e4SLinus Torvalds static void cleanup_sbc_gxx(void)
1711da177e4SLinus Torvalds {
1721da177e4SLinus Torvalds if( all_mtd ) {
173ee0e87b1SJamie Iles mtd_device_unregister(all_mtd);
1741da177e4SLinus Torvalds map_destroy( all_mtd );
1751da177e4SLinus Torvalds }
1761da177e4SLinus Torvalds
1771da177e4SLinus Torvalds iounmap(iomapadr);
1781da177e4SLinus Torvalds release_region(PAGE_IO,PAGE_IO_SIZE);
1791da177e4SLinus Torvalds }
1801da177e4SLinus Torvalds
init_sbc_gxx(void)1811da177e4SLinus Torvalds static int __init init_sbc_gxx(void)
1821da177e4SLinus Torvalds {
1831da177e4SLinus Torvalds iomapadr = ioremap(WINDOW_START, WINDOW_LENGTH);
1841da177e4SLinus Torvalds if (!iomapadr) {
1851da177e4SLinus Torvalds printk( KERN_ERR"%s: failed to ioremap memory region\n",
1861da177e4SLinus Torvalds sbc_gxx_map.name );
1871da177e4SLinus Torvalds return -EIO;
1881da177e4SLinus Torvalds }
1891da177e4SLinus Torvalds
1901da177e4SLinus Torvalds if (!request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash")) {
1911da177e4SLinus Torvalds printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n",
1921da177e4SLinus Torvalds sbc_gxx_map.name,
1931da177e4SLinus Torvalds PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 );
1941da177e4SLinus Torvalds iounmap(iomapadr);
1951da177e4SLinus Torvalds return -EAGAIN;
1961da177e4SLinus Torvalds }
1971da177e4SLinus Torvalds
1981da177e4SLinus Torvalds
1991da177e4SLinus Torvalds printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n",
2001da177e4SLinus Torvalds sbc_gxx_map.name,
2011da177e4SLinus Torvalds PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1,
2021da177e4SLinus Torvalds WINDOW_START, WINDOW_START+WINDOW_LENGTH-1 );
2031da177e4SLinus Torvalds
2041da177e4SLinus Torvalds /* Probe for chip. */
2051da177e4SLinus Torvalds all_mtd = do_map_probe( "cfi_probe", &sbc_gxx_map );
2061da177e4SLinus Torvalds if( !all_mtd ) {
2071da177e4SLinus Torvalds cleanup_sbc_gxx();
2081da177e4SLinus Torvalds return -ENXIO;
2091da177e4SLinus Torvalds }
2101da177e4SLinus Torvalds
2111da177e4SLinus Torvalds all_mtd->owner = THIS_MODULE;
2121da177e4SLinus Torvalds
2131da177e4SLinus Torvalds /* Create MTD devices for each partition. */
214ee0e87b1SJamie Iles mtd_device_register(all_mtd, partition_info, NUM_PARTITIONS);
2151da177e4SLinus Torvalds
2161da177e4SLinus Torvalds return 0;
2171da177e4SLinus Torvalds }
2181da177e4SLinus Torvalds
2191da177e4SLinus Torvalds module_init(init_sbc_gxx);
2201da177e4SLinus Torvalds module_exit(cleanup_sbc_gxx);
2211da177e4SLinus Torvalds
2221da177e4SLinus Torvalds MODULE_LICENSE("GPL");
2231da177e4SLinus Torvalds MODULE_AUTHOR("Arcom Control Systems Ltd.");
2241da177e4SLinus Torvalds MODULE_DESCRIPTION("MTD map driver for SBC-GXm and SBC-GX1 series boards");
225