2005-04-16 15:20:36 -07:00
/*
* IBM PowerPC Virtual I / O Infrastructure Support .
*
2005-07-12 17:50:26 +10:00
* Copyright ( c ) 2003 - 2005 IBM Corp .
2005-04-16 15:20:36 -07:00
* Dave Engebretsen engebret @ us . ibm . com
* Santiago Leon santil @ us . ibm . com
* Hollis Blanchard < hollisb @ us . ibm . com >
2005-07-12 17:50:26 +10:00
* Stephen Rothwell
2005-04-16 15:20:36 -07: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 .
*/
2006-04-27 17:23:32 +10:00
# include <linux/types.h>
# include <linux/device.h>
2005-04-16 15:20:36 -07:00
# include <linux/init.h>
# include <linux/console.h>
# include <linux/module.h>
# include <linux/mm.h>
# include <linux/dma-mapping.h>
2006-04-27 17:23:32 +10:00
# include <linux/kobject.h>
2005-04-16 15:20:36 -07:00
# include <asm/iommu.h>
# include <asm/dma.h>
# include <asm/vio.h>
2005-11-08 21:34:36 -08:00
# include <asm/prom.h>
2006-04-27 17:18:21 +10:00
# include <asm/firmware.h>
2006-04-27 17:23:32 +10:00
# include <asm/tce.h>
# include <asm/abs_addr.h>
# include <asm/page.h>
# include <asm/hvcall.h>
# include <asm/iseries/vio.h>
# include <asm/iseries/hv_types.h>
# include <asm/iseries/hv_lp_config.h>
# include <asm/iseries/hv_call_xm.h>
# include <asm/iseries/iommu.h>
extern struct subsystem devices_subsys ; /* needed for vio_find_name() */
static struct vio_dev vio_bus_device = { /* fake "parent" device */
2005-06-21 17:15:54 -07:00
. name = vio_bus_device . dev . bus_id ,
. type = " " ,
. dev . bus_id = " vio " ,
. dev . bus = & vio_bus_type ,
2005-04-16 15:20:36 -07:00
} ;
2005-06-21 17:15:54 -07:00
2006-04-27 17:23:32 +10:00
# ifdef CONFIG_PPC_ISERIES
struct device * iSeries_vio_dev = & vio_bus_device . dev ;
EXPORT_SYMBOL ( iSeries_vio_dev ) ;
static struct iommu_table veth_iommu_table ;
static struct iommu_table vio_iommu_table ;
static void __init iommu_vio_init ( void )
{
iommu_table_getparms_iSeries ( 255 , 0 , 0xff , & veth_iommu_table ) ;
veth_iommu_table . it_size / = 2 ;
vio_iommu_table = veth_iommu_table ;
vio_iommu_table . it_offset + = veth_iommu_table . it_size ;
2006-06-10 20:58:08 +10:00
if ( ! iommu_init_table ( & veth_iommu_table , - 1 ) )
2006-04-27 17:23:32 +10:00
printk ( " Virtual Bus VETH TCE table failed. \n " ) ;
2006-06-10 20:58:08 +10:00
if ( ! iommu_init_table ( & vio_iommu_table , - 1 ) )
2006-04-27 17:23:32 +10:00
printk ( " Virtual Bus VIO TCE table failed. \n " ) ;
}
# endif
static struct iommu_table * vio_build_iommu_table ( struct vio_dev * dev )
{
# ifdef CONFIG_PPC_ISERIES
if ( firmware_has_feature ( FW_FEATURE_ISERIES ) ) {
2006-05-18 17:21:26 +10:00
if ( strcmp ( dev - > type , " network " ) = = 0 )
2006-04-27 17:23:32 +10:00
return & veth_iommu_table ;
return & vio_iommu_table ;
} else
# endif
{
2006-07-12 15:35:54 +10:00
const unsigned char * dma_window ;
2006-05-18 18:06:37 +10:00
struct iommu_table * tbl ;
unsigned long offset , size ;
2006-04-27 17:23:32 +10:00
2007-04-03 22:26:41 +10:00
dma_window = of_get_property ( dev - > dev . archdata . of_node ,
2006-11-11 17:25:02 +11:00
" ibm,my-dma-window " , NULL ) ;
2006-04-27 17:23:32 +10:00
if ( ! dma_window )
return NULL ;
2006-05-18 18:06:37 +10:00
tbl = kmalloc ( sizeof ( * tbl ) , GFP_KERNEL ) ;
2006-04-27 17:23:32 +10:00
2006-11-11 17:25:02 +11:00
of_parse_dma_window ( dev - > dev . archdata . of_node , dma_window ,
& tbl - > it_index , & offset , & size ) ;
2006-04-27 17:23:32 +10:00
/* TCE table size - measured in tce entries */
2006-10-30 16:15:59 +11:00
tbl - > it_size = size > > IOMMU_PAGE_SHIFT ;
2006-04-27 17:23:32 +10:00
/* offset for VIO should always be 0 */
2006-10-30 16:15:59 +11:00
tbl - > it_offset = offset > > IOMMU_PAGE_SHIFT ;
2006-05-18 18:06:37 +10:00
tbl - > it_busno = 0 ;
tbl - > it_type = TCE_VB ;
2006-04-27 17:23:32 +10:00
2006-06-10 20:58:08 +10:00
return iommu_init_table ( tbl , - 1 ) ;
2006-04-27 17:23:32 +10:00
}
}
2005-04-16 15:20:36 -07:00
2006-04-27 17:18:21 +10:00
/**
* vio_match_device : - Tell if a VIO device has a matching
* VIO device id structure .
* @ ids : array of VIO device id structures to search in
* @ dev : the VIO device structure to match against
*
* Used by a driver to check whether a VIO device present in the
* system is in its list of supported devices . Returns the matching
* vio_device_id structure or NULL if there is no match .
*/
static const struct vio_device_id * vio_match_device (
const struct vio_device_id * ids , const struct vio_dev * dev )
{
while ( ids - > type [ 0 ] ! = ' \0 ' ) {
2006-04-27 17:21:46 +10:00
if ( ( strncmp ( dev - > type , ids - > type , strlen ( ids - > type ) ) = = 0 ) & &
2006-11-11 17:25:02 +11:00
device_is_compatible ( dev - > dev . archdata . of_node ,
ids - > compat ) )
2006-04-27 17:18:21 +10:00
return ids ;
ids + + ;
}
return NULL ;
}
2005-08-17 16:37:35 +10:00
/*
* Convert from struct device to struct vio_dev and pass to driver .
2005-04-16 15:20:36 -07:00
* dev - > driver has already been set by generic code because vio_bus_match
2005-08-17 16:37:35 +10:00
* succeeded .
*/
2005-04-16 15:20:36 -07:00
static int vio_bus_probe ( struct device * dev )
{
struct vio_dev * viodev = to_vio_dev ( dev ) ;
struct vio_driver * viodrv = to_vio_driver ( dev - > driver ) ;
const struct vio_device_id * id ;
int error = - ENODEV ;
if ( ! viodrv - > probe )
return error ;
id = vio_match_device ( viodrv - > id_table , viodev ) ;
2005-08-17 16:37:35 +10:00
if ( id )
2005-04-16 15:20:36 -07:00
error = viodrv - > probe ( viodev , id ) ;
return error ;
}
/* convert from struct device to struct vio_dev and pass to driver. */
static int vio_bus_remove ( struct device * dev )
{
struct vio_dev * viodev = to_vio_dev ( dev ) ;
struct vio_driver * viodrv = to_vio_driver ( dev - > driver ) ;
2005-08-17 16:37:35 +10:00
if ( viodrv - > remove )
2005-04-16 15:20:36 -07:00
return viodrv - > remove ( viodev ) ;
/* driver can't remove */
return 1 ;
}
2005-10-24 17:40:23 +10:00
/* convert from struct device to struct vio_dev and pass to driver. */
static void vio_bus_shutdown ( struct device * dev )
{
struct vio_dev * viodev = to_vio_dev ( dev ) ;
struct vio_driver * viodrv = to_vio_driver ( dev - > driver ) ;
2006-01-05 14:36:47 +00:00
if ( dev - > driver & & viodrv - > shutdown )
2005-10-24 17:40:23 +10:00
viodrv - > shutdown ( viodev ) ;
}
2005-04-16 15:20:36 -07:00
/**
* vio_register_driver : - Register a new vio driver
* @ drv : The vio_driver structure to be registered .
*/
int vio_register_driver ( struct vio_driver * viodrv )
{
printk ( KERN_DEBUG " %s: driver %s registering \n " , __FUNCTION__ ,
2005-10-24 14:53:21 +10:00
viodrv - > driver . name ) ;
2005-04-16 15:20:36 -07:00
/* fill in 'struct driver' fields */
viodrv - > driver . bus = & vio_bus_type ;
return driver_register ( & viodrv - > driver ) ;
}
EXPORT_SYMBOL ( vio_register_driver ) ;
/**
* vio_unregister_driver - Remove registration of vio driver .
* @ driver : The vio_driver struct to be removed form registration
*/
void vio_unregister_driver ( struct vio_driver * viodrv )
{
driver_unregister ( & viodrv - > driver ) ;
}
EXPORT_SYMBOL ( vio_unregister_driver ) ;
2006-04-27 17:23:32 +10:00
/* vio_dev refcount hit 0 */
static void __devinit vio_dev_release ( struct device * dev )
{
2007-01-02 12:38:36 +01:00
/* XXX should free TCE table */
of_node_put ( dev - > archdata . of_node ) ;
2006-04-27 17:23:32 +10:00
kfree ( to_vio_dev ( dev ) ) ;
}
2005-04-16 15:20:36 -07:00
/**
2006-04-27 17:18:21 +10:00
* vio_register_device_node : - Register a new vio device .
* @ of_node : The OF node for this device .
2005-04-16 15:20:36 -07:00
*
2006-04-27 17:18:21 +10:00
* Creates and initializes a vio_dev structure from the data in
2006-11-11 17:25:02 +11:00
* of_node and adds it to the list of virtual devices .
2006-04-27 17:18:21 +10:00
* Returns a pointer to the created vio_dev or NULL if node has
* NULL device_type or compatible fields .
2005-04-16 15:20:36 -07:00
*/
2006-04-27 17:18:21 +10:00
struct vio_dev * __devinit vio_register_device_node ( struct device_node * of_node )
2005-04-16 15:20:36 -07:00
{
2006-04-27 17:18:21 +10:00
struct vio_dev * viodev ;
2006-07-12 15:35:54 +10:00
const unsigned int * unit_address ;
2006-04-27 17:18:21 +10:00
/* we need the 'device_type' property, in order to match with drivers */
if ( of_node - > type = = NULL ) {
printk ( KERN_WARNING " %s: node %s missing 'device_type' \n " ,
__FUNCTION__ ,
of_node - > name ? of_node - > name : " <unknown> " ) ;
return NULL ;
2005-04-16 15:20:36 -07:00
}
2006-04-27 17:18:21 +10:00
2007-04-03 22:26:41 +10:00
unit_address = of_get_property ( of_node , " reg " , NULL ) ;
2006-04-27 17:18:21 +10:00
if ( unit_address = = NULL ) {
printk ( KERN_WARNING " %s: node %s missing 'reg' \n " ,
__FUNCTION__ ,
of_node - > name ? of_node - > name : " <unknown> " ) ;
return NULL ;
}
/* allocate a vio_dev for this node */
viodev = kzalloc ( sizeof ( struct vio_dev ) , GFP_KERNEL ) ;
if ( viodev = = NULL )
return NULL ;
2006-07-03 21:36:01 +10:00
viodev - > irq = irq_of_parse_and_map ( of_node , 0 ) ;
2006-04-27 17:18:21 +10:00
snprintf ( viodev - > dev . bus_id , BUS_ID_SIZE , " %x " , * unit_address ) ;
viodev - > name = of_node - > name ;
viodev - > type = of_node - > type ;
viodev - > unit_address = * unit_address ;
if ( firmware_has_feature ( FW_FEATURE_ISERIES ) ) {
2007-04-03 22:26:41 +10:00
unit_address = of_get_property ( of_node ,
2006-04-27 17:18:21 +10:00
" linux,unit_address " , NULL ) ;
if ( unit_address ! = NULL )
viodev - > unit_address = * unit_address ;
}
2006-11-11 17:25:02 +11:00
viodev - > dev . archdata . of_node = of_node_get ( of_node ) ;
viodev - > dev . archdata . dma_ops = & dma_iommu_ops ;
viodev - > dev . archdata . dma_data = vio_build_iommu_table ( viodev ) ;
viodev - > dev . archdata . numa_node = of_node_to_nid ( of_node ) ;
2006-04-27 17:23:32 +10:00
/* init generic 'struct device' fields: */
viodev - > dev . parent = & vio_bus_device . dev ;
viodev - > dev . bus = & vio_bus_type ;
viodev - > dev . release = vio_dev_release ;
2006-04-27 17:18:21 +10:00
/* register with generic device framework */
2006-04-27 17:23:32 +10:00
if ( device_register ( & viodev - > dev ) ) {
printk ( KERN_ERR " %s: failed to register device %s \n " ,
__FUNCTION__ , viodev - > dev . bus_id ) ;
2006-04-27 17:18:21 +10:00
/* XXX free TCE table */
kfree ( viodev ) ;
return NULL ;
}
return viodev ;
2005-04-16 15:20:36 -07:00
}
2006-04-27 17:18:21 +10:00
EXPORT_SYMBOL ( vio_register_device_node ) ;
2005-04-16 15:20:36 -07:00
/**
* vio_bus_init : - Initialize the virtual IO bus
*/
2006-04-27 17:23:32 +10:00
static int __init vio_bus_init ( void )
2005-04-16 15:20:36 -07:00
{
int err ;
2006-04-27 17:18:21 +10:00
struct device_node * node_vroot ;
2005-04-16 15:20:36 -07:00
2006-04-27 17:23:32 +10:00
# ifdef CONFIG_PPC_ISERIES
if ( firmware_has_feature ( FW_FEATURE_ISERIES ) ) {
iommu_vio_init ( ) ;
2006-11-11 17:25:02 +11:00
vio_bus_device . dev . archdata . dma_ops = & dma_iommu_ops ;
vio_bus_device . dev . archdata . dma_data = & vio_iommu_table ;
2006-04-27 17:23:32 +10:00
iSeries_vio_dev = & vio_bus_device . dev ;
}
2006-11-11 17:25:02 +11:00
# endif /* CONFIG_PPC_ISERIES */
2005-07-12 17:45:27 +10:00
2005-04-16 15:20:36 -07:00
err = bus_register ( & vio_bus_type ) ;
if ( err ) {
printk ( KERN_ERR " failed to register VIO bus \n " ) ;
return err ;
}
2005-08-17 16:37:35 +10:00
/*
* The fake parent of all vio devices , just to give us
2005-07-12 17:40:17 +10:00
* a nice directory
*/
2005-06-21 17:15:54 -07:00
err = device_register ( & vio_bus_device . dev ) ;
2005-04-16 15:20:36 -07:00
if ( err ) {
2005-07-12 17:40:17 +10:00
printk ( KERN_WARNING " %s: device_register returned %i \n " ,
__FUNCTION__ , err ) ;
2005-04-16 15:20:36 -07:00
return err ;
}
2007-04-24 13:53:04 +10:00
node_vroot = of_find_node_by_name ( NULL , " vdevice " ) ;
2006-04-27 17:18:21 +10:00
if ( node_vroot ) {
struct device_node * of_node ;
/*
* Create struct vio_devices for each virtual device in
* the device tree . Drivers will associate with them later .
*/
for ( of_node = node_vroot - > child ; of_node ! = NULL ;
of_node = of_node - > sibling ) {
printk ( KERN_DEBUG " %s: processing %p \n " ,
__FUNCTION__ , of_node ) ;
vio_register_device_node ( of_node ) ;
}
2007-04-24 13:53:04 +10:00
of_node_put ( node_vroot ) ;
2006-04-27 17:18:21 +10:00
}
2005-07-12 17:40:17 +10:00
return 0 ;
}
2006-04-27 17:23:32 +10:00
__initcall ( vio_bus_init ) ;
2005-04-16 15:20:36 -07:00
2006-04-27 17:18:21 +10:00
static ssize_t name_show ( struct device * dev ,
2005-08-17 16:37:35 +10:00
struct device_attribute * attr , char * buf )
2005-04-16 15:20:36 -07:00
{
return sprintf ( buf , " %s \n " , to_vio_dev ( dev ) - > name ) ;
}
2006-04-27 17:18:21 +10:00
static ssize_t devspec_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
2006-11-11 17:25:02 +11:00
struct device_node * of_node = dev - > archdata . of_node ;
2006-04-27 17:18:21 +10:00
return sprintf ( buf , " %s \n " , of_node ? of_node - > full_name : " none " ) ;
}
static struct device_attribute vio_dev_attrs [ ] = {
__ATTR_RO ( name ) ,
__ATTR_RO ( devspec ) ,
__ATTR_NULL
} ;
2005-04-16 15:20:36 -07:00
void __devinit vio_unregister_device ( struct vio_dev * viodev )
{
device_unregister ( & viodev - > dev ) ;
}
EXPORT_SYMBOL ( vio_unregister_device ) ;
static int vio_bus_match ( struct device * dev , struct device_driver * drv )
{
const struct vio_dev * vio_dev = to_vio_dev ( dev ) ;
struct vio_driver * vio_drv = to_vio_driver ( drv ) ;
const struct vio_device_id * ids = vio_drv - > id_table ;
2005-08-17 16:37:35 +10:00
return ( ids ! = NULL ) & & ( vio_match_device ( ids , vio_dev ) ! = NULL ) ;
2005-04-16 15:20:36 -07:00
}
2005-11-08 21:34:36 -08:00
static int vio_hotplug ( struct device * dev , char * * envp , int num_envp ,
char * buffer , int buffer_size )
{
const struct vio_dev * vio_dev = to_vio_dev ( dev ) ;
2006-11-11 17:25:02 +11:00
struct device_node * dn ;
2006-07-12 15:35:54 +10:00
const char * cp ;
2005-11-08 21:34:36 -08:00
int length ;
if ( ! num_envp )
return - ENOMEM ;
2006-11-11 17:25:02 +11:00
dn = dev - > archdata . of_node ;
2006-04-27 17:18:21 +10:00
if ( ! dn )
2005-11-08 21:34:36 -08:00
return - ENODEV ;
2007-04-03 22:26:41 +10:00
cp = of_get_property ( dn , " compatible " , & length ) ;
2005-11-08 21:34:36 -08:00
if ( ! cp )
return - ENODEV ;
envp [ 0 ] = buffer ;
length = scnprintf ( buffer , buffer_size , " MODALIAS=vio:T%sS%s " ,
vio_dev - > type , cp ) ;
2006-04-27 17:18:21 +10:00
if ( ( buffer_size - length ) < = 0 )
2005-11-08 21:34:36 -08:00
return - ENOMEM ;
envp [ 1 ] = NULL ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
struct bus_type vio_bus_type = {
. name = " vio " ,
2006-04-27 17:18:21 +10:00
. dev_attrs = vio_dev_attrs ,
2005-11-16 09:00:00 +01:00
. uevent = vio_hotplug ,
2005-04-16 15:20:36 -07:00
. match = vio_bus_match ,
2006-01-05 14:36:47 +00:00
. probe = vio_bus_probe ,
. remove = vio_bus_remove ,
. shutdown = vio_bus_shutdown ,
2005-04-16 15:20:36 -07:00
} ;
2006-04-27 17:18:21 +10:00
/**
* vio_get_attribute : - get attribute for virtual device
* @ vdev : The vio device to get property .
* @ which : The property / attribute to be extracted .
* @ length : Pointer to length of returned data size ( unused if NULL ) .
*
2007-04-03 22:26:41 +10:00
* Calls prom . c ' s of_get_property ( ) to return the value of the
2006-04-27 17:18:21 +10:00
* attribute specified by @ which
*/
const void * vio_get_attribute ( struct vio_dev * vdev , char * which , int * length )
{
2007-04-03 22:26:41 +10:00
return of_get_property ( vdev - > dev . archdata . of_node , which , length ) ;
2006-04-27 17:18:21 +10:00
}
EXPORT_SYMBOL ( vio_get_attribute ) ;
2006-04-27 17:23:32 +10:00
# ifdef CONFIG_PPC_PSERIES
/* vio_find_name() - internal because only vio.c knows how we formatted the
* kobject name
* XXX once vio_bus_type . devices is actually used as a kset in
* drivers / base / bus . c , this function should be removed in favor of
* " device_find(kobj_name, &vio_bus_type) "
*/
static struct vio_dev * vio_find_name ( const char * kobj_name )
{
struct kobject * found ;
found = kset_find_obj ( & devices_subsys . kset , kobj_name ) ;
if ( ! found )
return NULL ;
return to_vio_dev ( container_of ( found , struct device , kobj ) ) ;
}
/**
* vio_find_node - find an already - registered vio_dev
* @ vnode : device_node of the virtual device we ' re looking for
*/
struct vio_dev * vio_find_node ( struct device_node * vnode )
{
2006-07-12 15:35:54 +10:00
const uint32_t * unit_address ;
2006-04-27 17:23:32 +10:00
char kobj_name [ BUS_ID_SIZE ] ;
/* construct the kobject name from the device node */
2007-04-03 22:26:41 +10:00
unit_address = of_get_property ( vnode , " reg " , NULL ) ;
2006-04-27 17:23:32 +10:00
if ( ! unit_address )
return NULL ;
snprintf ( kobj_name , BUS_ID_SIZE , " %x " , * unit_address ) ;
return vio_find_name ( kobj_name ) ;
}
EXPORT_SYMBOL ( vio_find_node ) ;
int vio_enable_interrupts ( struct vio_dev * dev )
{
int rc = h_vio_signal ( dev - > unit_address , VIO_IRQ_ENABLE ) ;
if ( rc ! = H_SUCCESS )
printk ( KERN_ERR " vio: Error 0x%x enabling interrupts \n " , rc ) ;
return rc ;
}
EXPORT_SYMBOL ( vio_enable_interrupts ) ;
int vio_disable_interrupts ( struct vio_dev * dev )
{
int rc = h_vio_signal ( dev - > unit_address , VIO_IRQ_DISABLE ) ;
if ( rc ! = H_SUCCESS )
printk ( KERN_ERR " vio: Error 0x%x disabling interrupts \n " , rc ) ;
return rc ;
}
EXPORT_SYMBOL ( vio_disable_interrupts ) ;
# endif /* CONFIG_PPC_PSERIES */