2005-04-16 15:20:36 -07:00
/**
2006-05-14 01:51:54 +01:00
* Copyright ( c ) ? ? ? ? Jochen Schäuble < psionic @ psionic . de >
2008-02-06 01:38:02 -08:00
* Copyright ( c ) 2003 - 2004 Joern Engel < joern @ wh . fh - wedel . de >
2005-04-16 15:20:36 -07:00
*
* Usage :
*
* one commend line parameter per device , each in the form :
* phram = < name > , < start > , < len >
* < name > may be up to 63 characters .
* < start > and < len > can be octal , decimal or hexadecimal . If followed
* by " ki " , " Mi " or " Gi " , the numbers will be interpreted as kilo , mega or
* gigabytes .
*
* Example :
* phram = swap , 64 Mi , 128 Mi phram = test , 900 Mi , 1 Mi
*/
2009-06-16 19:20:40 +00:00
2010-10-20 10:39:22 -07:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2009-06-16 19:20:40 +00:00
2005-04-16 15:20:36 -07:00
# include <asm/io.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
2005-10-30 15:03:48 -08:00
# include <linux/slab.h>
2005-04-16 15:20:36 -07:00
# include <linux/mtd/mtd.h>
struct phram_mtd_list {
struct mtd_info mtd ;
struct list_head list ;
} ;
static LIST_HEAD ( phram_list ) ;
static int phram_erase ( struct mtd_info * mtd , struct erase_info * instr )
{
u_char * start = mtd - > priv ;
if ( instr - > addr + instr - > len > mtd - > size )
return - EINVAL ;
2005-11-07 11:15:40 +00:00
2005-04-16 15:20:36 -07:00
memset ( start + instr - > addr , 0xff , instr - > len ) ;
2005-11-07 11:15:40 +00:00
/* This'll catch a few races. Free the thing before returning :)
2005-04-16 15:20:36 -07:00
* I don ' t feel at all ashamed . This kind of thing is possible anyway
* with flash , but unlikely .
*/
instr - > state = MTD_ERASE_DONE ;
mtd_erase_callback ( instr ) ;
return 0 ;
}
static int phram_point ( struct mtd_info * mtd , loff_t from , size_t len ,
2008-04-29 23:26:49 -07:00
size_t * retlen , void * * virt , resource_size_t * phys )
2005-04-16 15:20:36 -07:00
{
if ( from + len > mtd - > size )
return - EINVAL ;
2005-11-07 11:15:40 +00:00
2008-04-29 23:26:49 -07:00
/* can we return a physical address with this driver? */
if ( phys )
return - EINVAL ;
* virt = mtd - > priv + from ;
2005-04-16 15:20:36 -07:00
* retlen = len ;
return 0 ;
}
2008-04-29 23:26:49 -07:00
static void phram_unpoint ( struct mtd_info * mtd , loff_t from , size_t len )
2005-04-16 15:20:36 -07:00
{
}
static int phram_read ( struct mtd_info * mtd , loff_t from , size_t len ,
size_t * retlen , u_char * buf )
{
u_char * start = mtd - > priv ;
2005-03-07 21:43:42 +00:00
if ( from > = mtd - > size )
2005-04-16 15:20:36 -07:00
return - EINVAL ;
2005-03-07 21:43:42 +00:00
if ( len > mtd - > size - from )
len = mtd - > size - from ;
2005-11-07 11:15:40 +00:00
2005-04-16 15:20:36 -07:00
memcpy ( buf , start + from , len ) ;
* retlen = len ;
return 0 ;
}
static int phram_write ( struct mtd_info * mtd , loff_t to , size_t len ,
size_t * retlen , const u_char * buf )
{
u_char * start = mtd - > priv ;
2005-03-07 21:43:42 +00:00
if ( to > = mtd - > size )
2005-04-16 15:20:36 -07:00
return - EINVAL ;
2005-03-07 21:43:42 +00:00
if ( len > mtd - > size - to )
len = mtd - > size - to ;
2005-11-07 11:15:40 +00:00
2005-04-16 15:20:36 -07:00
memcpy ( start + to , buf , len ) ;
* retlen = len ;
return 0 ;
}
static void unregister_devices ( void )
{
2005-02-23 19:37:11 +00:00
struct phram_mtd_list * this , * safe ;
2005-04-16 15:20:36 -07:00
2005-02-23 19:37:11 +00:00
list_for_each_entry_safe ( this , safe , & phram_list , list ) {
2011-05-23 10:23:40 +01:00
mtd_device_unregister ( & this - > mtd ) ;
2005-04-16 15:20:36 -07:00
iounmap ( this - > mtd . priv ) ;
2011-01-30 10:31:48 +01:00
kfree ( this - > mtd . name ) ;
2005-04-16 15:20:36 -07:00
kfree ( this ) ;
}
}
static int register_device ( char * name , unsigned long start , unsigned long len )
{
struct phram_mtd_list * new ;
int ret = - ENOMEM ;
2006-11-15 21:10:29 +02:00
new = kzalloc ( sizeof ( * new ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! new )
goto out0 ;
ret = - EIO ;
new - > mtd . priv = ioremap ( start , len ) ;
if ( ! new - > mtd . priv ) {
2009-06-16 19:20:40 +00:00
pr_err ( " ioremap failed \n " ) ;
2005-04-16 15:20:36 -07:00
goto out1 ;
}
new - > mtd . name = name ;
new - > mtd . size = len ;
2006-04-13 18:54:34 +02:00
new - > mtd . flags = MTD_CAP_RAM ;
2005-04-16 15:20:36 -07:00
new - > mtd . erase = phram_erase ;
new - > mtd . point = phram_point ;
new - > mtd . unpoint = phram_unpoint ;
new - > mtd . read = phram_read ;
new - > mtd . write = phram_write ;
new - > mtd . owner = THIS_MODULE ;
2006-06-14 21:39:48 +01:00
new - > mtd . type = MTD_RAM ;
2005-03-07 21:43:42 +00:00
new - > mtd . erasesize = PAGE_SIZE ;
2006-06-22 18:15:48 +04:00
new - > mtd . writesize = 1 ;
2005-04-16 15:20:36 -07:00
ret = - EAGAIN ;
2011-05-23 10:23:40 +01:00
if ( mtd_device_register ( & new - > mtd , NULL , 0 ) ) {
2009-06-16 19:20:40 +00:00
pr_err ( " Failed to register new device \n " ) ;
2005-04-16 15:20:36 -07:00
goto out2 ;
}
list_add_tail ( & new - > list , & phram_list ) ;
2005-11-07 11:15:40 +00:00
return 0 ;
2005-04-16 15:20:36 -07:00
out2 :
iounmap ( new - > mtd . priv ) ;
out1 :
kfree ( new ) ;
out0 :
return ret ;
}
static int ustrtoul ( const char * cp , char * * endp , unsigned int base )
{
unsigned long result = simple_strtoul ( cp , endp , base ) ;
switch ( * * endp ) {
case ' G ' :
result * = 1024 ;
case ' M ' :
result * = 1024 ;
case ' k ' :
result * = 1024 ;
/* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
if ( ( * endp ) [ 1 ] = = ' i ' )
( * endp ) + = 2 ;
}
return result ;
}
static int parse_num32 ( uint32_t * num32 , const char * token )
{
char * endp ;
unsigned long n ;
n = ustrtoul ( token , & endp , 0 ) ;
if ( * endp )
return - EINVAL ;
* num32 = n ;
return 0 ;
}
static int parse_name ( char * * pname , const char * token )
{
size_t len ;
char * name ;
len = strlen ( token ) + 1 ;
if ( len > 64 )
return - ENOSPC ;
name = kmalloc ( len , GFP_KERNEL ) ;
if ( ! name )
return - ENOMEM ;
strcpy ( name , token ) ;
* pname = name ;
return 0 ;
}
2005-03-07 21:43:42 +00:00
static inline void kill_final_newline ( char * str )
{
char * newline = strrchr ( str , ' \n ' ) ;
if ( newline & & ! newline [ 1 ] )
* newline = 0 ;
}
2005-04-16 15:20:36 -07:00
# define parse_err(fmt, args...) do { \
2009-06-16 19:20:40 +00:00
pr_err ( fmt , # # args ) ; \
return 1 ; \
2005-04-16 15:20:36 -07:00
} while ( 0 )
static int phram_setup ( const char * val , struct kernel_param * kp )
{
char buf [ 64 + 12 + 12 ] , * str = buf ;
char * token [ 3 ] ;
char * name ;
uint32_t start ;
uint32_t len ;
int i , ret ;
if ( strnlen ( val , sizeof ( buf ) ) > = sizeof ( buf ) )
parse_err ( " parameter too long \n " ) ;
strcpy ( str , val ) ;
2005-03-07 21:43:42 +00:00
kill_final_newline ( str ) ;
2005-04-16 15:20:36 -07:00
for ( i = 0 ; i < 3 ; i + + )
token [ i ] = strsep ( & str , " , " ) ;
if ( str )
parse_err ( " too many arguments \n " ) ;
if ( ! token [ 2 ] )
parse_err ( " not enough arguments \n " ) ;
ret = parse_name ( & name , token [ 0 ] ) ;
if ( ret )
2009-06-16 19:20:40 +00:00
return ret ;
2005-04-16 15:20:36 -07:00
ret = parse_num32 ( & start , token [ 1 ] ) ;
2006-05-14 01:07:18 +02:00
if ( ret ) {
kfree ( name ) ;
2005-04-16 15:20:36 -07:00
parse_err ( " illegal start address \n " ) ;
2006-05-14 01:07:18 +02:00
}
2005-04-16 15:20:36 -07:00
ret = parse_num32 ( & len , token [ 2 ] ) ;
2006-05-14 01:07:18 +02:00
if ( ret ) {
kfree ( name ) ;
2005-04-16 15:20:36 -07:00
parse_err ( " illegal device length \n " ) ;
2006-05-14 01:07:18 +02:00
}
2005-04-16 15:20:36 -07:00
2009-06-16 19:20:40 +00:00
ret = register_device ( name , start , len ) ;
if ( ! ret )
pr_info ( " %s device: %#x at %#x \n " , name , len , start ) ;
2011-01-30 10:31:48 +01:00
else
kfree ( name ) ;
2005-04-16 15:20:36 -07:00
2009-06-16 19:20:40 +00:00
return ret ;
2005-04-16 15:20:36 -07:00
}
module_param_call ( phram , phram_setup , NULL , NULL , 000 ) ;
2008-03-31 14:25:03 +01:00
MODULE_PARM_DESC ( phram , " Memory region to map. \" phram=<name>,<start>,<length> \" " ) ;
2005-04-16 15:20:36 -07:00
static int __init init_phram ( void )
{
return 0 ;
}
static void __exit cleanup_phram ( void )
{
unregister_devices ( ) ;
}
module_init ( init_phram ) ;
module_exit ( cleanup_phram ) ;
MODULE_LICENSE ( " GPL " ) ;
2008-02-06 01:38:02 -08:00
MODULE_AUTHOR ( " Joern Engel <joern@wh.fh-wedel.de> " ) ;
2005-04-16 15:20:36 -07:00
MODULE_DESCRIPTION ( " MTD driver for physical RAM " ) ;