2005-04-17 02:20:36 +04:00
/* sbc_gxx.c -- MTD map driver for Arcom Control Systems SBC-MediaGX,
SBC - GXm and SBC - GX1 series boards .
2005-11-07 14:15:40 +03:00
2005-04-17 02:20:36 +04:00
Copyright ( C ) 2001 Arcom Control System Ltd
2005-11-07 14:15:40 +03:00
2005-04-17 02:20:36 +04:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
2005-11-07 14:15:40 +03:00
2005-04-17 02:20:36 +04:00
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
2005-11-07 14:15:40 +03:00
2005-04-17 02:20:36 +04:00
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA
2005-11-07 14:15:40 +03:00
The SBC - MediaGX / SBC - GXx has up to 16 MiB of
Intel StrataFlash ( 28F 320 / 28F 640 ) in x8 mode .
2005-04-17 02:20:36 +04:00
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
2005-11-07 14:15:40 +03:00
2005-04-17 02:20:36 +04:00
Two IO address locations for paging
2005-11-07 14:15:40 +03:00
2005-04-17 02:20:36 +04:00
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
2005-11-07 14:15:40 +03:00
The single flash device is divided into 3 partition which appear as
2005-04-17 02:20:36 +04:00
separate MTD devices .
25 / 04 / 2001 AJL ( Arcom ) Modified signon strings and partition sizes
( to support bzImages up to 638 KiB - 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 ) ;
2005-11-07 14:15:40 +03:00
/* partition_info gives details on the logical partitions that the split the
2005-04-17 02:20:36 +04:00
* single flash device into . If the size if zero we use up to the end of the
* device . */
static struct mtd_partition partition_info [ ] = {
2005-11-07 14:15:40 +03:00
{ . name = " SBC-GXx flash boot partition " ,
. offset = 0 ,
2005-04-17 02:20:36 +04:00
. size = BOOT_PARTITION_SIZE_KiB * 1024 } ,
2005-11-07 14:15:40 +03:00
{ . name = " SBC-GXx flash data partition " ,
. offset = BOOT_PARTITION_SIZE_KiB * 1024 ,
2005-04-17 02:20:36 +04:00
. size = ( DATA_PARTITION_SIZE_KiB ) * 1024 } ,
2005-11-07 14:15:40 +03:00
{ . name = " SBC-GXx flash application partition " ,
2005-04-17 02:20:36 +04:00
. 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 ) ;
2005-11-07 14:15:40 +03:00
2005-04-17 02:20:36 +04:00
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 )
2005-11-07 14:15:40 +03:00
{
2005-04-17 02:20:36 +04:00
while ( len ) {
unsigned long thislen = len ;
if ( len > ( WINDOW_LENGTH - ( to & WINDOW_MASK ) ) )
thislen = WINDOW_LENGTH - ( to & WINDOW_MASK ) ;
2005-11-07 14:15:40 +03:00
2005-04-17 02:20:36 +04:00
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 ) {
2011-05-23 13:23:40 +04:00
mtd_device_unregister ( all_mtd ) ;
2005-04-17 02:20:36 +04:00
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 ;
}
2005-11-07 14:15:40 +03:00
2005-04-17 02:20:36 +04:00
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 ;
}
2005-11-07 14:15:40 +03:00
2005-04-17 02:20:36 +04:00
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 ;
}
2005-11-07 14:15:40 +03:00
2005-04-17 02:20:36 +04:00
all_mtd - > owner = THIS_MODULE ;
/* Create MTD devices for each partition. */
2011-05-23 13:23:40 +04:00
mtd_device_register ( all_mtd , partition_info , NUM_PARTITIONS ) ;
2005-04-17 02:20:36 +04:00
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 " ) ;