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
2014-10-21 20:01:09 +01:00
# include <linux/io.h>
2005-04-16 15:20:36 -07:00
# 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 ;
memset ( start + instr - > addr , 0xff , instr - > len ) ;
2012-03-13 16:07:53 +01: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
{
2008-04-29 23:26:49 -07:00
* virt = mtd - > priv + from ;
2005-04-16 15:20:36 -07:00
* retlen = len ;
return 0 ;
}
2012-02-03 13:20:43 +02:00
static int phram_unpoint ( struct mtd_info * mtd , loff_t from , size_t len )
2005-04-16 15:20:36 -07:00
{
2012-02-03 13:20:43 +02:00
return 0 ;
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 ;
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 ;
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 ) ;
}
}
2013-10-02 18:42:29 +02:00
static int register_device ( char * name , phys_addr_t start , size_t len )
2005-04-16 15:20:36 -07:00
{
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 ;
2012-01-30 14:58:32 +02: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 ;
2005-04-16 15:20:36 -07:00
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 ;
}
2013-10-02 18:42:29 +02:00
static int parse_num64 ( uint64_t * num64 , char * token )
2005-04-16 15:20:36 -07:00
{
2013-10-02 18:42:29 +02:00
size_t len ;
int shift = 0 ;
int ret ;
len = strlen ( token ) ;
2005-04-16 15:20:36 -07:00
/* By dwmw2 editorial decree, "ki", "Mi" or "Gi" are to be used. */
2013-10-02 18:42:29 +02:00
if ( len > 2 ) {
if ( token [ len - 1 ] = = ' i ' ) {
switch ( token [ len - 2 ] ) {
case ' G ' :
shift + = 10 ;
case ' M ' :
shift + = 10 ;
case ' k ' :
shift + = 10 ;
token [ len - 2 ] = 0 ;
break ;
default :
return - EINVAL ;
}
}
2005-04-16 15:20:36 -07:00
}
2013-10-02 18:42:29 +02:00
ret = kstrtou64 ( token , 0 , num64 ) ;
* num64 < < = shift ;
2005-04-16 15:20:36 -07:00
2013-10-02 18:42:29 +02:00
return ret ;
2005-04-16 15:20:36 -07:00
}
static int parse_name ( char * * pname , const char * token )
{
size_t len ;
char * name ;
len = strlen ( token ) + 1 ;
if ( len > 64 )
return - ENOSPC ;
2014-05-28 22:31:28 +02:00
name = kstrdup ( token , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( ! name )
return - ENOMEM ;
* 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 ' ) ;
2014-07-13 20:39:01 +01:00
2005-03-07 21:43:42 +00:00
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 )
2013-10-14 18:52:23 +02:00
# ifndef MODULE
static int phram_init_called ;
2012-03-13 16:07:53 +01:00
/*
* This shall contain the module parameter if any . It is of the form :
* - phram = < device > , < address > , < size > for module case
* - phram . phram = < device > , < address > , < size > for built - in case
2013-10-02 18:42:29 +02:00
* We leave 64 bytes for the device name , 20 for the address and 20 for the
2012-03-13 16:07:53 +01:00
* size .
* Example : phram . phram = rootfs , 0xa0000000 , 512 Mi
*/
2013-10-14 18:52:23 +02:00
static char phram_paramline [ 64 + 20 + 20 ] ;
# endif
2012-03-13 16:07:53 +01:00
2013-10-14 18:52:23 +02:00
static int phram_setup ( const char * val )
2005-04-16 15:20:36 -07:00
{
2013-10-02 18:42:29 +02:00
char buf [ 64 + 20 + 20 ] , * str = buf ;
2005-04-16 15:20:36 -07:00
char * token [ 3 ] ;
char * name ;
2013-10-02 18:42:29 +02:00
uint64_t start ;
uint64_t len ;
2005-04-16 15:20:36 -07:00
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
2014-07-13 20:39:01 +01:00
for ( i = 0 ; i < 3 ; i + + )
2005-04-16 15:20:36 -07:00
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
2013-10-02 18:42:29 +02:00
ret = parse_num64 ( & 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
2013-10-02 18:42:29 +02:00
ret = parse_num64 ( & 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 )
2013-10-02 18:42:29 +02:00
pr_info ( " %s device: %#llx at %#llx \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
}
2013-10-14 18:52:23 +02:00
static int phram_param_call ( const char * val , struct kernel_param * kp )
2012-03-13 16:07:53 +01:00
{
2013-10-14 18:52:23 +02:00
# ifdef MODULE
return phram_setup ( val ) ;
# else
2012-03-13 16:07:53 +01:00
/*
2013-10-14 18:52:23 +02:00
* If more parameters are later passed in via
* / sys / module / phram / parameters / phram
* and init_phram ( ) has already been called ,
* we can parse the argument now .
2012-03-13 16:07:53 +01:00
*/
2013-10-14 18:52:23 +02:00
if ( phram_init_called )
return phram_setup ( val ) ;
/*
* During early boot stage , we only save the parameters
* here . We must parse them later : if the param passed
* from kernel boot command line , phram_param_call ( ) is
* called so early that it is not possible to resolve
* the device ( even kmalloc ( ) fails ) . Defer that work to
* phram_setup ( ) .
*/
2012-03-13 16:07:53 +01:00
if ( strlen ( val ) > = sizeof ( phram_paramline ) )
return - ENOSPC ;
strcpy ( phram_paramline , val ) ;
return 0 ;
2013-10-14 18:52:23 +02:00
# endif
2012-03-13 16:07:53 +01:00
}
module_param_call ( phram , phram_param_call , 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 )
{
2013-10-14 18:52:23 +02:00
int ret = 0 ;
# ifndef MODULE
2012-03-13 16:07:53 +01:00
if ( phram_paramline [ 0 ] )
2013-10-14 18:52:23 +02:00
ret = phram_setup ( phram_paramline ) ;
phram_init_called = 1 ;
# endif
2012-03-13 16:07:53 +01:00
2013-10-14 18:52:23 +02:00
return ret ;
2005-04-16 15:20:36 -07:00
}
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 " ) ;