forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
224 lines
5.6 KiB
224 lines
5.6 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
/* sbc_gxx.c -- MTD map driver for Arcom Control Systems SBC-MediaGX, |
|
SBC-GXm and SBC-GX1 series boards. |
|
|
|
Copyright (C) 2001 Arcom Control System Ltd |
|
|
|
|
|
The SBC-MediaGX / SBC-GXx has up to 16 MiB of |
|
Intel StrataFlash (28F320/28F640) in x8 mode. |
|
|
|
This driver uses the CFI probe and Intel Extended Command Set drivers. |
|
|
|
The flash is accessed as follows: |
|
|
|
16 KiB memory window at 0xdc000-0xdffff |
|
|
|
Two IO address locations for paging |
|
|
|
0x258 |
|
bit 0-7: address bit 14-21 |
|
0x259 |
|
bit 0-1: address bit 22-23 |
|
bit 7: 0 - reset/powered down |
|
1 - device enabled |
|
|
|
The single flash device is divided into 3 partition which appear as |
|
separate MTD devices. |
|
|
|
25/04/2001 AJL (Arcom) Modified signon strings and partition sizes |
|
(to support bzImages up to 638KiB-ish) |
|
*/ |
|
|
|
// Includes |
|
|
|
#include <linux/module.h> |
|
#include <linux/ioport.h> |
|
#include <linux/init.h> |
|
#include <asm/io.h> |
|
|
|
#include <linux/mtd/mtd.h> |
|
#include <linux/mtd/map.h> |
|
#include <linux/mtd/partitions.h> |
|
|
|
// Defines |
|
|
|
// - Hardware specific |
|
|
|
#define WINDOW_START 0xdc000 |
|
|
|
/* Number of bits in offset. */ |
|
#define WINDOW_SHIFT 14 |
|
#define WINDOW_LENGTH (1 << WINDOW_SHIFT) |
|
|
|
/* The bits for the offset into the window. */ |
|
#define WINDOW_MASK (WINDOW_LENGTH-1) |
|
#define PAGE_IO 0x258 |
|
#define PAGE_IO_SIZE 2 |
|
|
|
/* bit 7 of 0x259 must be 1 to enable device. */ |
|
#define DEVICE_ENABLE 0x8000 |
|
|
|
// - Flash / Partition sizing |
|
|
|
#define MAX_SIZE_KiB 16384 |
|
#define BOOT_PARTITION_SIZE_KiB 768 |
|
#define DATA_PARTITION_SIZE_KiB 1280 |
|
#define APP_PARTITION_SIZE_KiB 6144 |
|
|
|
// Globals |
|
|
|
static volatile int page_in_window = -1; // Current page in window. |
|
static void __iomem *iomapadr; |
|
static DEFINE_SPINLOCK(sbc_gxx_spin); |
|
|
|
/* partition_info gives details on the logical partitions that the split the |
|
* single flash device into. If the size if zero we use up to the end of the |
|
* device. */ |
|
static const struct mtd_partition partition_info[] = { |
|
{ .name = "SBC-GXx flash boot partition", |
|
.offset = 0, |
|
.size = BOOT_PARTITION_SIZE_KiB*1024 }, |
|
{ .name = "SBC-GXx flash data partition", |
|
.offset = BOOT_PARTITION_SIZE_KiB*1024, |
|
.size = (DATA_PARTITION_SIZE_KiB)*1024 }, |
|
{ .name = "SBC-GXx flash application partition", |
|
.offset = (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 } |
|
}; |
|
|
|
#define NUM_PARTITIONS 3 |
|
|
|
static inline void sbc_gxx_page(struct map_info *map, unsigned long ofs) |
|
{ |
|
unsigned long page = ofs >> WINDOW_SHIFT; |
|
|
|
if( page!=page_in_window ) { |
|
outw( page | DEVICE_ENABLE, PAGE_IO ); |
|
page_in_window = page; |
|
} |
|
} |
|
|
|
|
|
static map_word sbc_gxx_read8(struct map_info *map, unsigned long ofs) |
|
{ |
|
map_word ret; |
|
spin_lock(&sbc_gxx_spin); |
|
sbc_gxx_page(map, ofs); |
|
ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK)); |
|
spin_unlock(&sbc_gxx_spin); |
|
return ret; |
|
} |
|
|
|
static void sbc_gxx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) |
|
{ |
|
while(len) { |
|
unsigned long thislen = len; |
|
if (len > (WINDOW_LENGTH - (from & WINDOW_MASK))) |
|
thislen = WINDOW_LENGTH-(from & WINDOW_MASK); |
|
|
|
spin_lock(&sbc_gxx_spin); |
|
sbc_gxx_page(map, from); |
|
memcpy_fromio(to, iomapadr + (from & WINDOW_MASK), thislen); |
|
spin_unlock(&sbc_gxx_spin); |
|
to += thislen; |
|
from += thislen; |
|
len -= thislen; |
|
} |
|
} |
|
|
|
static void sbc_gxx_write8(struct map_info *map, map_word d, unsigned long adr) |
|
{ |
|
spin_lock(&sbc_gxx_spin); |
|
sbc_gxx_page(map, adr); |
|
writeb(d.x[0], iomapadr + (adr & WINDOW_MASK)); |
|
spin_unlock(&sbc_gxx_spin); |
|
} |
|
|
|
static void sbc_gxx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) |
|
{ |
|
while(len) { |
|
unsigned long thislen = len; |
|
if (len > (WINDOW_LENGTH - (to & WINDOW_MASK))) |
|
thislen = WINDOW_LENGTH-(to & WINDOW_MASK); |
|
|
|
spin_lock(&sbc_gxx_spin); |
|
sbc_gxx_page(map, to); |
|
memcpy_toio(iomapadr + (to & WINDOW_MASK), from, thislen); |
|
spin_unlock(&sbc_gxx_spin); |
|
to += thislen; |
|
from += thislen; |
|
len -= thislen; |
|
} |
|
} |
|
|
|
static struct map_info sbc_gxx_map = { |
|
.name = "SBC-GXx flash", |
|
.phys = NO_XIP, |
|
.size = MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount |
|
of flash so the cfi probe routines find all |
|
the chips */ |
|
.bankwidth = 1, |
|
.read = sbc_gxx_read8, |
|
.copy_from = sbc_gxx_copy_from, |
|
.write = sbc_gxx_write8, |
|
.copy_to = sbc_gxx_copy_to |
|
}; |
|
|
|
/* MTD device for all of the flash. */ |
|
static struct mtd_info *all_mtd; |
|
|
|
static void cleanup_sbc_gxx(void) |
|
{ |
|
if( all_mtd ) { |
|
mtd_device_unregister(all_mtd); |
|
map_destroy( all_mtd ); |
|
} |
|
|
|
iounmap(iomapadr); |
|
release_region(PAGE_IO,PAGE_IO_SIZE); |
|
} |
|
|
|
static int __init init_sbc_gxx(void) |
|
{ |
|
iomapadr = ioremap(WINDOW_START, WINDOW_LENGTH); |
|
if (!iomapadr) { |
|
printk( KERN_ERR"%s: failed to ioremap memory region\n", |
|
sbc_gxx_map.name ); |
|
return -EIO; |
|
} |
|
|
|
if (!request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash")) { |
|
printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n", |
|
sbc_gxx_map.name, |
|
PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); |
|
iounmap(iomapadr); |
|
return -EAGAIN; |
|
} |
|
|
|
|
|
printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n", |
|
sbc_gxx_map.name, |
|
PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1, |
|
WINDOW_START, WINDOW_START+WINDOW_LENGTH-1 ); |
|
|
|
/* Probe for chip. */ |
|
all_mtd = do_map_probe( "cfi_probe", &sbc_gxx_map ); |
|
if( !all_mtd ) { |
|
cleanup_sbc_gxx(); |
|
return -ENXIO; |
|
} |
|
|
|
all_mtd->owner = THIS_MODULE; |
|
|
|
/* Create MTD devices for each partition. */ |
|
mtd_device_register(all_mtd, partition_info, NUM_PARTITIONS); |
|
|
|
return 0; |
|
} |
|
|
|
module_init(init_sbc_gxx); |
|
module_exit(cleanup_sbc_gxx); |
|
|
|
MODULE_LICENSE("GPL"); |
|
MODULE_AUTHOR("Arcom Control Systems Ltd."); |
|
MODULE_DESCRIPTION("MTD map driver for SBC-GXm and SBC-GX1 series boards");
|
|
|