2008-08-27 12:13:12 +04:00
/* flash.c: Allow mmap access to the OBP Flash, for OBP updates.
2005-04-17 02:20:36 +04:00
*
* Copyright ( C ) 1997 Eddie C . Dost ( ecd @ skynet . be )
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/miscdevice.h>
# include <linux/slab.h>
# include <linux/fcntl.h>
# include <linux/poll.h>
# include <linux/init.h>
# include <linux/smp_lock.h>
# include <linux/spinlock.h>
2007-05-31 12:27:52 +04:00
# include <linux/mm.h>
2008-08-27 12:13:12 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
2005-04-17 02:20:36 +04:00
# include <asm/system.h>
# include <asm/uaccess.h>
# include <asm/pgtable.h>
# include <asm/io.h>
# include <asm/upa.h>
static DEFINE_SPINLOCK ( flash_lock ) ;
static struct {
unsigned long read_base ; /* Physical read address */
unsigned long write_base ; /* Physical write address */
unsigned long read_size ; /* Size of read area */
unsigned long write_size ; /* Size of write area */
unsigned long busy ; /* In use? */
} flash ;
# define FLASH_MINOR 152
static int
flash_mmap ( struct file * file , struct vm_area_struct * vma )
{
unsigned long addr ;
unsigned long size ;
spin_lock ( & flash_lock ) ;
if ( flash . read_base = = flash . write_base ) {
addr = flash . read_base ;
size = flash . read_size ;
} else {
if ( ( vma - > vm_flags & VM_READ ) & &
( vma - > vm_flags & VM_WRITE ) ) {
spin_unlock ( & flash_lock ) ;
return - EINVAL ;
}
if ( vma - > vm_flags & VM_READ ) {
addr = flash . read_base ;
size = flash . read_size ;
} else if ( vma - > vm_flags & VM_WRITE ) {
addr = flash . write_base ;
size = flash . write_size ;
} else {
spin_unlock ( & flash_lock ) ;
return - ENXIO ;
}
}
spin_unlock ( & flash_lock ) ;
if ( ( vma - > vm_pgoff < < PAGE_SHIFT ) > size )
return - ENXIO ;
addr = vma - > vm_pgoff + ( addr > > PAGE_SHIFT ) ;
if ( vma - > vm_end - ( vma - > vm_start + ( vma - > vm_pgoff < < PAGE_SHIFT ) ) > size )
size = vma - > vm_end - ( vma - > vm_start + ( vma - > vm_pgoff < < PAGE_SHIFT ) ) ;
2006-03-21 13:29:39 +03:00
vma - > vm_page_prot = pgprot_noncached ( vma - > vm_page_prot ) ;
2005-04-17 02:20:36 +04:00
if ( io_remap_pfn_range ( vma , vma - > vm_start , addr , size , vma - > vm_page_prot ) )
return - EAGAIN ;
return 0 ;
}
static long long
flash_llseek ( struct file * file , long long offset , int origin )
{
lock_kernel ( ) ;
switch ( origin ) {
case 0 :
file - > f_pos = offset ;
break ;
case 1 :
file - > f_pos + = offset ;
if ( file - > f_pos > flash . read_size )
file - > f_pos = flash . read_size ;
break ;
case 2 :
file - > f_pos = flash . read_size ;
break ;
default :
unlock_kernel ( ) ;
return - EINVAL ;
}
unlock_kernel ( ) ;
return file - > f_pos ;
}
static ssize_t
flash_read ( struct file * file , char __user * buf ,
size_t count , loff_t * ppos )
{
unsigned long p = file - > f_pos ;
int i ;
if ( count > flash . read_size - p )
count = flash . read_size - p ;
for ( i = 0 ; i < count ; i + + ) {
u8 data = upa_readb ( flash . read_base + p + i ) ;
if ( put_user ( data , buf ) )
return - EFAULT ;
buf + + ;
}
file - > f_pos + = count ;
return count ;
}
static int
flash_open ( struct inode * inode , struct file * file )
{
2008-05-20 21:15:54 +04:00
lock_kernel ( ) ;
if ( test_and_set_bit ( 0 , ( void * ) & flash . busy ) ! = 0 ) {
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
2008-05-20 21:15:54 +04:00
}
2005-04-17 02:20:36 +04:00
2008-05-20 21:15:54 +04:00
unlock_kernel ( ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static int
flash_release ( struct inode * inode , struct file * file )
{
spin_lock ( & flash_lock ) ;
flash . busy = 0 ;
spin_unlock ( & flash_lock ) ;
return 0 ;
}
2007-02-12 11:55:34 +03:00
static const struct file_operations flash_fops = {
2005-04-17 02:20:36 +04:00
/* no write to the Flash, use mmap
* and play flash dependent tricks .
*/
. owner = THIS_MODULE ,
. llseek = flash_llseek ,
. read = flash_read ,
. mmap = flash_mmap ,
. open = flash_open ,
. release = flash_release ,
} ;
static struct miscdevice flash_dev = { FLASH_MINOR , " flash " , & flash_fops } ;
2008-08-27 12:13:12 +04:00
static int __devinit flash_probe ( struct of_device * op ,
const struct of_device_id * match )
2005-04-17 02:20:36 +04:00
{
2008-08-27 12:13:12 +04:00
struct device_node * dp = op - > node ;
struct device_node * parent ;
2005-04-17 02:20:36 +04:00
2008-08-27 12:13:12 +04:00
parent = dp - > parent ;
2005-04-17 02:20:36 +04:00
2008-08-27 12:13:12 +04:00
if ( strcmp ( parent - > name , " sbus " ) & &
strcmp ( parent - > name , " sbi " ) & &
strcmp ( parent - > name , " ebus " ) )
2005-04-17 02:20:36 +04:00
return - ENODEV ;
2008-08-27 12:13:12 +04:00
flash . read_base = op - > resource [ 0 ] . start ;
flash . read_size = resource_size ( & op - > resource [ 0 ] ) ;
if ( op - > resource [ 1 ] . flags ) {
flash . write_base = op - > resource [ 1 ] . start ;
flash . write_size = resource_size ( & op - > resource [ 1 ] ) ;
} else {
flash . write_base = op - > resource [ 0 ] . start ;
flash . write_size = resource_size ( & op - > resource [ 0 ] ) ;
2005-04-17 02:20:36 +04:00
}
2008-08-27 12:13:12 +04:00
flash . busy = 0 ;
2005-04-17 02:20:36 +04:00
2008-08-27 12:13:12 +04:00
printk ( KERN_INFO " %s: OBP Flash, RD %lx[%lx] WR %lx[%lx] \n " ,
op - > node - > full_name ,
2005-04-17 02:20:36 +04:00
flash . read_base , flash . read_size ,
flash . write_base , flash . write_size ) ;
2008-08-27 12:13:12 +04:00
return misc_register ( & flash_dev ) ;
}
static int __devexit flash_remove ( struct of_device * op )
{
misc_deregister ( & flash_dev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2008-08-31 12:23:17 +04:00
static const struct of_device_id flash_match [ ] = {
2008-08-27 12:13:12 +04:00
{
. name = " flashprom " ,
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , flash_match ) ;
static struct of_platform_driver flash_driver = {
. name = " flash " ,
. match_table = flash_match ,
. probe = flash_probe ,
. remove = __devexit_p ( flash_remove ) ,
} ;
static int __init flash_init ( void )
{
return of_register_driver ( & flash_driver , & of_bus_type ) ;
}
2005-04-17 02:20:36 +04:00
static void __exit flash_cleanup ( void )
{
2008-08-27 12:13:12 +04:00
of_unregister_driver ( & flash_driver ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( flash_init ) ;
module_exit ( flash_cleanup ) ;
MODULE_LICENSE ( " GPL " ) ;