2018-09-26 02:16:19 +03:00
// SPDX-License-Identifier: GPL-2.0+
/*
* VFIO based AP device driver
*
* Copyright IBM Corp . 2018
*
* Author ( s ) : Tony Krowiak < akrowiak @ linux . ibm . com >
2019-05-21 18:34:36 +03:00
* Pierre Morel < pmorel @ linux . ibm . com >
2018-09-26 02:16:19 +03:00
*/
# include <linux/module.h>
# include <linux/mod_devicetable.h>
# include <linux/slab.h>
# include <linux/string.h>
2018-11-16 13:47:48 +03:00
# include <asm/facility.h>
2018-09-26 02:16:19 +03:00
# include "vfio_ap_private.h"
# define VFIO_AP_ROOT_NAME "vfio_ap"
# define VFIO_AP_DEV_NAME "matrix"
MODULE_AUTHOR ( " IBM Corporation " ) ;
MODULE_DESCRIPTION ( " VFIO AP device driver, Copyright IBM Corp. 2018 " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
static struct ap_driver vfio_ap_drv ;
struct ap_matrix_dev * matrix_dev ;
/* Only type 10 adapters (CEX4 and later) are supported
* by the AP matrix device driver
*/
static struct ap_device_id ap_queue_ids [ ] = {
{ . dev_type = AP_DEVICE_TYPE_CEX4 ,
. match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE } ,
{ . dev_type = AP_DEVICE_TYPE_CEX5 ,
. match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE } ,
{ . dev_type = AP_DEVICE_TYPE_CEX6 ,
. match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE } ,
2019-08-16 12:05:58 +03:00
{ . dev_type = AP_DEVICE_TYPE_CEX7 ,
. match_flags = AP_DEVICE_ID_MATCH_QUEUE_TYPE } ,
2018-09-26 02:16:19 +03:00
{ /* end of sibling */ } ,
} ;
MODULE_DEVICE_TABLE ( vfio_ap , ap_queue_ids ) ;
2019-05-21 18:34:36 +03:00
/**
* vfio_ap_queue_dev_probe :
*
* Allocate a vfio_ap_queue structure and associate it
* with the device as driver_data .
*/
2018-09-26 02:16:19 +03:00
static int vfio_ap_queue_dev_probe ( struct ap_device * apdev )
{
2019-05-21 18:34:36 +03:00
struct vfio_ap_queue * q ;
q = kzalloc ( sizeof ( * q ) , GFP_KERNEL ) ;
if ( ! q )
return - ENOMEM ;
dev_set_drvdata ( & apdev - > device , q ) ;
q - > apqn = to_ap_queue ( & apdev - > device ) - > qid ;
q - > saved_isc = VFIO_AP_ISC_INVALID ;
2018-09-26 02:16:19 +03:00
return 0 ;
}
2019-05-21 18:34:36 +03:00
/**
* vfio_ap_queue_dev_remove :
*
* Takes the matrix lock to avoid actions on this device while removing
* Free the associated vfio_ap_queue structure
*/
2018-09-26 02:16:19 +03:00
static void vfio_ap_queue_dev_remove ( struct ap_device * apdev )
{
2019-05-21 18:34:36 +03:00
struct vfio_ap_queue * q ;
int apid , apqi ;
mutex_lock ( & matrix_dev - > lock ) ;
q = dev_get_drvdata ( & apdev - > device ) ;
dev_set_drvdata ( & apdev - > device , NULL ) ;
apid = AP_QID_CARD ( q - > apqn ) ;
apqi = AP_QID_QUEUE ( q - > apqn ) ;
vfio_ap_mdev_reset_queue ( apid , apqi , 1 ) ;
vfio_ap_irq_disable ( q ) ;
kfree ( q ) ;
mutex_unlock ( & matrix_dev - > lock ) ;
2018-09-26 02:16:19 +03:00
}
static void vfio_ap_matrix_dev_release ( struct device * dev )
{
struct ap_matrix_dev * matrix_dev = dev_get_drvdata ( dev ) ;
kfree ( matrix_dev ) ;
}
2019-02-12 18:53:45 +03:00
static int matrix_bus_match ( struct device * dev , struct device_driver * drv )
{
return 1 ;
}
static struct bus_type matrix_bus = {
. name = " matrix " ,
. match = & matrix_bus_match ,
} ;
static struct device_driver matrix_driver = {
. name = " vfio_ap " ,
. bus = & matrix_bus ,
. suppress_bind_attrs = true ,
} ;
2018-09-26 02:16:19 +03:00
static int vfio_ap_matrix_dev_create ( void )
{
int ret ;
struct device * root_device ;
root_device = root_device_register ( VFIO_AP_ROOT_NAME ) ;
if ( IS_ERR ( root_device ) )
return PTR_ERR ( root_device ) ;
2019-02-12 18:53:45 +03:00
ret = bus_register ( & matrix_bus ) ;
if ( ret )
goto bus_register_err ;
2018-09-26 02:16:19 +03:00
matrix_dev = kzalloc ( sizeof ( * matrix_dev ) , GFP_KERNEL ) ;
if ( ! matrix_dev ) {
ret = - ENOMEM ;
goto matrix_alloc_err ;
}
2018-09-26 02:16:20 +03:00
/* Fill in config info via PQAP(QCI), if available */
if ( test_facility ( 12 ) ) {
ret = ap_qci ( & matrix_dev - > info ) ;
if ( ret )
goto matrix_alloc_err ;
}
mutex_init ( & matrix_dev - > lock ) ;
INIT_LIST_HEAD ( & matrix_dev - > mdev_list ) ;
2018-09-26 02:16:19 +03:00
dev_set_name ( & matrix_dev - > device , " %s " , VFIO_AP_DEV_NAME ) ;
matrix_dev - > device . parent = root_device ;
2019-02-12 18:53:45 +03:00
matrix_dev - > device . bus = & matrix_bus ;
2018-09-26 02:16:19 +03:00
matrix_dev - > device . release = vfio_ap_matrix_dev_release ;
2019-02-12 18:53:45 +03:00
matrix_dev - > vfio_ap_drv = & vfio_ap_drv ;
2018-09-26 02:16:19 +03:00
ret = device_register ( & matrix_dev - > device ) ;
if ( ret )
goto matrix_reg_err ;
2019-02-12 18:53:45 +03:00
ret = driver_register ( & matrix_driver ) ;
if ( ret )
goto matrix_drv_err ;
2018-09-26 02:16:19 +03:00
return 0 ;
2019-02-12 18:53:45 +03:00
matrix_drv_err :
device_unregister ( & matrix_dev - > device ) ;
2018-09-26 02:16:19 +03:00
matrix_reg_err :
put_device ( & matrix_dev - > device ) ;
matrix_alloc_err :
2019-02-12 18:53:45 +03:00
bus_unregister ( & matrix_bus ) ;
bus_register_err :
2018-09-26 02:16:19 +03:00
root_device_unregister ( root_device ) ;
return ret ;
}
static void vfio_ap_matrix_dev_destroy ( void )
{
2019-02-12 18:53:45 +03:00
struct device * root_device = matrix_dev - > device . parent ;
driver_unregister ( & matrix_driver ) ;
2018-09-26 02:16:19 +03:00
device_unregister ( & matrix_dev - > device ) ;
2019-02-12 18:53:45 +03:00
bus_unregister ( & matrix_bus ) ;
root_device_unregister ( root_device ) ;
2018-09-26 02:16:19 +03:00
}
2018-10-05 20:22:38 +03:00
static int __init vfio_ap_init ( void )
2018-09-26 02:16:19 +03:00
{
int ret ;
/* If there are no AP instructions, there is nothing to pass through. */
if ( ! ap_instructions_available ( ) )
return - ENODEV ;
ret = vfio_ap_matrix_dev_create ( ) ;
if ( ret )
return ret ;
memset ( & vfio_ap_drv , 0 , sizeof ( vfio_ap_drv ) ) ;
vfio_ap_drv . probe = vfio_ap_queue_dev_probe ;
vfio_ap_drv . remove = vfio_ap_queue_dev_remove ;
vfio_ap_drv . ids = ap_queue_ids ;
ret = ap_driver_register ( & vfio_ap_drv , THIS_MODULE , VFIO_AP_DRV_NAME ) ;
if ( ret ) {
vfio_ap_matrix_dev_destroy ( ) ;
return ret ;
}
2018-09-26 02:16:20 +03:00
ret = vfio_ap_mdev_register ( ) ;
if ( ret ) {
ap_driver_unregister ( & vfio_ap_drv ) ;
vfio_ap_matrix_dev_destroy ( ) ;
return ret ;
}
2018-09-26 02:16:19 +03:00
return 0 ;
}
2018-10-05 20:22:38 +03:00
static void __exit vfio_ap_exit ( void )
2018-09-26 02:16:19 +03:00
{
2018-09-26 02:16:20 +03:00
vfio_ap_mdev_unregister ( ) ;
2018-09-26 02:16:19 +03:00
ap_driver_unregister ( & vfio_ap_drv ) ;
vfio_ap_matrix_dev_destroy ( ) ;
}
module_init ( vfio_ap_init ) ;
module_exit ( vfio_ap_exit ) ;