2005-04-17 02:20:36 +04:00
/*
* drivers / sh / superhyway / superhyway . c
*
* SuperHyway Bus Driver
*
* Copyright ( C ) 2004 , 2005 Paul Mundt < lethal @ linux - sh . org >
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
# include <linux/kernel.h>
# include <linux/device.h>
# include <linux/init.h>
# include <linux/module.h>
# include <linux/types.h>
# include <linux/list.h>
# include <linux/superhyway.h>
2005-10-31 02:03:48 +03:00
# include <linux/string.h>
# include <linux/slab.h>
2005-04-17 02:20:36 +04:00
static int superhyway_devices ;
static struct device superhyway_bus_device = {
2009-03-03 06:16:12 +03:00
. init_name = " superhyway " ,
2005-04-17 02:20:36 +04:00
} ;
static void superhyway_device_release ( struct device * dev )
{
2005-11-07 11:58:21 +03:00
struct superhyway_device * sdev = to_superhyway_device ( dev ) ;
kfree ( sdev - > resource ) ;
kfree ( sdev ) ;
2005-04-17 02:20:36 +04:00
}
/**
* superhyway_add_device - Add a SuperHyway module
* @ base : Physical address where module is mapped .
2005-11-07 11:58:21 +03:00
* @ sdev : SuperHyway device to add , or NULL to allocate a new one .
* @ bus : Bus where SuperHyway module resides .
2005-04-17 02:20:36 +04:00
*
* This is responsible for adding a new SuperHyway module . This sets up a new
2005-11-07 11:58:21 +03:00
* struct superhyway_device for the module being added if @ sdev = = NULL .
2005-04-17 02:20:36 +04:00
*
* Devices are initially added in the order that they are scanned ( from the
* top - down of the memory map ) , and are assigned an ID based on the order that
* they are added . Any manual addition of a module will thus get the ID after
* the devices already discovered regardless of where it resides in memory .
*
* Further work can and should be done in superhyway_scan_bus ( ) , to be sure
* that any new modules are properly discovered and subsequently registered .
*/
2005-11-07 11:58:21 +03:00
int superhyway_add_device ( unsigned long base , struct superhyway_device * sdev ,
struct superhyway_bus * bus )
2005-04-17 02:20:36 +04:00
{
2005-11-07 11:58:21 +03:00
struct superhyway_device * dev = sdev ;
if ( ! dev ) {
some kmalloc/memset ->kzalloc (tree wide)
Transform some calls to kmalloc/memset to a single kzalloc (or kcalloc).
Here is a short excerpt of the semantic patch performing
this transformation:
@@
type T2;
expression x;
identifier f,fld;
expression E;
expression E1,E2;
expression e1,e2,e3,y;
statement S;
@@
x =
- kmalloc
+ kzalloc
(E1,E2)
... when != \(x->fld=E;\|y=f(...,x,...);\|f(...,x,...);\|x=E;\|while(...) S\|for(e1;e2;e3) S\)
- memset((T2)x,0,E1);
@@
expression E1,E2,E3;
@@
- kzalloc(E1 * E2,E3)
+ kcalloc(E1,E2,E3)
[akpm@linux-foundation.org: get kcalloc args the right way around]
Signed-off-by: Yoann Padioleau <padator@wanadoo.fr>
Cc: Richard Henderson <rth@twiddle.net>
Cc: Ivan Kokshaysky <ink@jurassic.park.msu.ru>
Acked-by: Russell King <rmk@arm.linux.org.uk>
Cc: Bryan Wu <bryan.wu@analog.com>
Acked-by: Jiri Slaby <jirislaby@gmail.com>
Cc: Dave Airlie <airlied@linux.ie>
Acked-by: Roland Dreier <rolandd@cisco.com>
Cc: Jiri Kosina <jkosina@suse.cz>
Acked-by: Dmitry Torokhov <dtor@mail.ru>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Acked-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Acked-by: Pierre Ossman <drzeus-list@drzeus.cx>
Cc: Jeff Garzik <jeff@garzik.org>
Cc: "David S. Miller" <davem@davemloft.net>
Acked-by: Greg KH <greg@kroah.com>
Cc: James Bottomley <James.Bottomley@steeleye.com>
Cc: "Antonino A. Daplas" <adaplas@pol.net>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2007-07-19 12:49:03 +04:00
dev = kzalloc ( sizeof ( struct superhyway_device ) , GFP_KERNEL ) ;
2005-11-07 11:58:21 +03:00
if ( ! dev )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
2005-11-07 11:58:21 +03:00
}
2005-04-17 02:20:36 +04:00
2005-11-07 11:58:21 +03:00
dev - > bus = bus ;
superhyway_read_vcr ( dev , base , & dev - > vcr ) ;
2005-04-17 02:20:36 +04:00
2005-11-07 11:58:21 +03:00
if ( ! dev - > resource ) {
dev - > resource = kmalloc ( sizeof ( struct resource ) , GFP_KERNEL ) ;
if ( ! dev - > resource ) {
kfree ( dev ) ;
return - ENOMEM ;
}
dev - > resource - > name = dev - > name ;
dev - > resource - > start = base ;
dev - > resource - > end = dev - > resource - > start + 0x01000000 ;
}
2005-04-17 02:20:36 +04:00
dev - > dev . parent = & superhyway_bus_device ;
dev - > dev . bus = & superhyway_bus_type ;
dev - > dev . release = superhyway_device_release ;
2005-11-07 11:58:21 +03:00
dev - > id . id = dev - > vcr . mod_id ;
2005-04-17 02:20:36 +04:00
2005-11-07 11:58:21 +03:00
sprintf ( dev - > name , " SuperHyway device %04x " , dev - > id . id ) ;
2009-03-03 06:16:12 +03:00
dev_set_name ( & dev - > dev , " %02x " , superhyway_devices ) ;
2005-04-17 02:20:36 +04:00
superhyway_devices + + ;
return device_register ( & dev - > dev ) ;
}
2005-11-07 11:58:21 +03:00
int superhyway_add_devices ( struct superhyway_bus * bus ,
struct superhyway_device * * devices ,
int nr_devices )
{
int i , ret = 0 ;
for ( i = 0 ; i < nr_devices ; i + + ) {
struct superhyway_device * dev = devices [ i ] ;
ret | = superhyway_add_device ( dev - > resource [ 0 ] . start , dev , bus ) ;
}
return ret ;
}
2005-04-17 02:20:36 +04:00
static int __init superhyway_init ( void )
{
2005-11-07 11:58:21 +03:00
struct superhyway_bus * bus ;
2007-11-06 12:05:08 +03:00
int ret ;
2005-11-07 11:58:21 +03:00
2007-11-06 12:05:08 +03:00
ret = device_register ( & superhyway_bus_device ) ;
if ( unlikely ( ret ) )
return ret ;
2005-11-07 11:58:21 +03:00
for ( bus = superhyway_channels ; bus - > ops ; bus + + )
ret | = superhyway_scan_bus ( bus ) ;
return ret ;
2005-04-17 02:20:36 +04:00
}
postcore_initcall ( superhyway_init ) ;
static const struct superhyway_device_id *
superhyway_match_id ( const struct superhyway_device_id * ids ,
struct superhyway_device * dev )
{
while ( ids - > id ) {
if ( ids - > id = = dev - > id . id )
return ids ;
ids + + ;
}
return NULL ;
}
static int superhyway_device_probe ( struct device * dev )
{
struct superhyway_device * shyway_dev = to_superhyway_device ( dev ) ;
struct superhyway_driver * shyway_drv = to_superhyway_driver ( dev - > driver ) ;
if ( shyway_drv & & shyway_drv - > probe ) {
const struct superhyway_device_id * id ;
id = superhyway_match_id ( shyway_drv - > id_table , shyway_dev ) ;
if ( id )
return shyway_drv - > probe ( shyway_dev , id ) ;
}
return - ENODEV ;
}
static int superhyway_device_remove ( struct device * dev )
{
struct superhyway_device * shyway_dev = to_superhyway_device ( dev ) ;
struct superhyway_driver * shyway_drv = to_superhyway_driver ( dev - > driver ) ;
if ( shyway_drv & & shyway_drv - > remove ) {
shyway_drv - > remove ( shyway_dev ) ;
return 0 ;
}
return - ENODEV ;
}
/**
* superhyway_register_driver - Register a new SuperHyway driver
* @ drv : SuperHyway driver to register .
*
* This registers the passed in @ drv . Any devices matching the id table will
* automatically be populated and handed off to the driver ' s specified probe
* routine .
*/
int superhyway_register_driver ( struct superhyway_driver * drv )
{
drv - > drv . name = drv - > name ;
drv - > drv . bus = & superhyway_bus_type ;
return driver_register ( & drv - > drv ) ;
}
/**
* superhyway_unregister_driver - Unregister a SuperHyway driver
* @ drv : SuperHyway driver to unregister .
*
* This cleans up after superhyway_register_driver ( ) , and should be invoked in
* the exit path of any module drivers .
*/
void superhyway_unregister_driver ( struct superhyway_driver * drv )
{
driver_unregister ( & drv - > drv ) ;
}
static int superhyway_bus_match ( struct device * dev , struct device_driver * drv )
{
struct superhyway_device * shyway_dev = to_superhyway_device ( dev ) ;
struct superhyway_driver * shyway_drv = to_superhyway_driver ( drv ) ;
const struct superhyway_device_id * ids = shyway_drv - > id_table ;
if ( ! ids )
return - EINVAL ;
if ( superhyway_match_id ( ids , shyway_dev ) )
return 1 ;
return - ENODEV ;
}
struct bus_type superhyway_bus_type = {
. name = " superhyway " ,
. match = superhyway_bus_match ,
# ifdef CONFIG_SYSFS
. dev_attrs = superhyway_dev_attrs ,
# endif
2006-01-05 17:42:40 +03:00
. probe = superhyway_device_probe ,
. remove = superhyway_device_remove ,
2005-04-17 02:20:36 +04:00
} ;
static int __init superhyway_bus_init ( void )
{
return bus_register ( & superhyway_bus_type ) ;
}
static void __exit superhyway_bus_exit ( void )
{
device_unregister ( & superhyway_bus_device ) ;
bus_unregister ( & superhyway_bus_type ) ;
}
core_initcall ( superhyway_bus_init ) ;
module_exit ( superhyway_bus_exit ) ;
EXPORT_SYMBOL ( superhyway_bus_type ) ;
EXPORT_SYMBOL ( superhyway_add_device ) ;
2005-11-07 11:58:21 +03:00
EXPORT_SYMBOL ( superhyway_add_devices ) ;
2005-04-17 02:20:36 +04:00
EXPORT_SYMBOL ( superhyway_register_driver ) ;
EXPORT_SYMBOL ( superhyway_unregister_driver ) ;
MODULE_LICENSE ( " GPL " ) ;