2005-04-16 15:20:36 -07:00
/*
* File : portdrv_pci . c
* Purpose : PCI Express Port Bus Driver
*
* Copyright ( C ) 2004 Intel
* Copyright ( C ) Tom Long Nguyen ( tom . l . nguyen @ intel . com )
*/
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/pm.h>
# include <linux/init.h>
2005-10-30 15:03:48 -08:00
# include <linux/slab.h>
2005-04-16 15:20:36 -07:00
# include <linux/pcieport_if.h>
2006-07-31 15:26:16 +08:00
# include <linux/aer.h>
2005-04-16 15:20:36 -07:00
# include "portdrv.h"
2006-07-31 15:26:16 +08:00
# include "aer/aerdrv.h"
2005-04-16 15:20:36 -07:00
/*
* Version Information
*/
# define DRIVER_VERSION "v1.0"
# define DRIVER_AUTHOR "tom.l.nguyen@intel.com"
# define DRIVER_DESC "PCIE Port Bus Driver"
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
/* global data */
2006-07-31 15:26:16 +08:00
static int pcie_portdrv_restore_config ( struct pci_dev * dev )
{
int retval ;
retval = pci_enable_device ( dev ) ;
if ( retval )
return retval ;
pci_set_master ( dev ) ;
return 0 ;
}
2006-09-28 14:35:59 +08:00
# ifdef CONFIG_PM
2009-02-15 22:32:48 +01:00
static struct dev_pm_ops pcie_portdrv_pm_ops = {
. suspend = pcie_port_device_suspend ,
. resume = pcie_port_device_resume ,
. freeze = pcie_port_device_suspend ,
. thaw = pcie_port_device_resume ,
. poweroff = pcie_port_device_suspend ,
. restore = pcie_port_device_resume ,
} ;
2006-07-31 15:26:16 +08:00
2009-02-15 22:32:48 +01:00
# define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops)
2008-12-27 16:28:58 +01:00
2009-02-15 22:32:48 +01:00
# else /* !PM */
# define PCIE_PORTDRV_PM_OPS NULL
# endif /* !PM */
2006-07-31 15:26:16 +08:00
2005-04-16 15:20:36 -07:00
/*
* pcie_portdrv_probe - Probe PCI - Express port devices
* @ dev : PCI - Express port device being probed
*
* If detected invokes the pcie_port_device_register ( ) method for
* this port device .
*
*/
static int __devinit pcie_portdrv_probe ( struct pci_dev * dev ,
const struct pci_device_id * id )
{
int status ;
status = pcie_port_device_probe ( dev ) ;
if ( status )
return status ;
2006-12-06 22:07:30 +09:00
if ( ! dev - > irq & & dev - > pin ) {
2008-08-25 15:45:20 -06:00
dev_warn ( & dev - > dev , " device [%04x:%04x] has invalid IRQ; "
2008-06-13 10:52:13 -06:00
" check vendor BIOS \n " , dev - > vendor , dev - > device ) ;
2005-04-16 15:20:36 -07:00
}
2009-01-13 14:42:01 +01:00
status = pcie_port_device_register ( dev ) ;
if ( status )
return status ;
2005-04-16 15:20:36 -07:00
2009-01-13 14:43:07 +01:00
pci_save_state ( dev ) ;
2006-07-31 15:26:16 +08:00
2005-04-16 15:20:36 -07:00
return 0 ;
}
static void pcie_portdrv_remove ( struct pci_dev * dev )
{
pcie_port_device_remove ( dev ) ;
2009-03-07 19:35:47 -07:00
pci_disable_device ( dev ) ;
2005-04-16 15:20:36 -07:00
}
2006-07-31 15:26:16 +08:00
static int error_detected_iter ( struct device * device , void * data )
2006-07-06 18:05:51 +04:00
{
2006-07-31 15:26:16 +08:00
struct pcie_device * pcie_device ;
struct pcie_port_service_driver * driver ;
struct aer_broadcast_data * result_data ;
pci_ers_result_t status ;
result_data = ( struct aer_broadcast_data * ) data ;
if ( device - > bus = = & pcie_port_bus_type & & device - > driver ) {
driver = to_service_driver ( device - > driver ) ;
if ( ! driver | |
! driver - > err_handler | |
! driver - > err_handler - > error_detected )
return 0 ;
pcie_device = to_pcie_device ( device ) ;
/* Forward error detected message to service drivers */
status = driver - > err_handler - > error_detected (
pcie_device - > port ,
result_data - > state ) ;
result_data - > result =
merge_result ( result_data - > result , status ) ;
}
return 0 ;
2006-07-06 18:05:51 +04:00
}
2006-07-31 15:26:16 +08:00
static pci_ers_result_t pcie_portdrv_error_detected ( struct pci_dev * dev ,
enum pci_channel_state error )
2006-07-06 18:05:51 +04:00
{
2006-07-31 15:26:16 +08:00
struct aer_broadcast_data result_data =
{ error , PCI_ERS_RESULT_CAN_RECOVER } ;
2006-08-28 11:43:25 -07:00
int retval ;
2006-07-31 15:26:16 +08:00
2006-08-28 11:43:25 -07:00
/* can not fail */
retval = device_for_each_child ( & dev - > dev , & result_data , error_detected_iter ) ;
2006-07-31 15:26:16 +08:00
return result_data . result ;
}
static int mmio_enabled_iter ( struct device * device , void * data )
{
struct pcie_device * pcie_device ;
struct pcie_port_service_driver * driver ;
pci_ers_result_t status , * result ;
result = ( pci_ers_result_t * ) data ;
if ( device - > bus = = & pcie_port_bus_type & & device - > driver ) {
driver = to_service_driver ( device - > driver ) ;
if ( driver & &
driver - > err_handler & &
driver - > err_handler - > mmio_enabled ) {
pcie_device = to_pcie_device ( device ) ;
/* Forward error message to service drivers */
status = driver - > err_handler - > mmio_enabled (
pcie_device - > port ) ;
* result = merge_result ( * result , status ) ;
}
}
2006-07-06 18:05:51 +04:00
return 0 ;
}
2006-07-31 15:26:16 +08:00
static pci_ers_result_t pcie_portdrv_mmio_enabled ( struct pci_dev * dev )
2005-04-16 15:20:36 -07:00
{
2006-07-31 15:26:16 +08:00
pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED ;
2006-08-28 11:43:25 -07:00
int retval ;
2005-06-22 09:09:54 -07:00
2006-08-28 11:43:25 -07:00
/* get true return value from &status */
retval = device_for_each_child ( & dev - > dev , & status , mmio_enabled_iter ) ;
2006-07-31 15:26:16 +08:00
return status ;
2005-04-16 15:20:36 -07:00
}
2006-07-31 15:26:16 +08:00
static int slot_reset_iter ( struct device * device , void * data )
2005-04-16 15:20:36 -07:00
{
2006-07-31 15:26:16 +08:00
struct pcie_device * pcie_device ;
struct pcie_port_service_driver * driver ;
pci_ers_result_t status , * result ;
result = ( pci_ers_result_t * ) data ;
if ( device - > bus = = & pcie_port_bus_type & & device - > driver ) {
driver = to_service_driver ( device - > driver ) ;
if ( driver & &
driver - > err_handler & &
driver - > err_handler - > slot_reset ) {
pcie_device = to_pcie_device ( device ) ;
/* Forward error message to service drivers */
status = driver - > err_handler - > slot_reset (
pcie_device - > port ) ;
* result = merge_result ( * result , status ) ;
}
}
return 0 ;
}
static pci_ers_result_t pcie_portdrv_slot_reset ( struct pci_dev * dev )
{
2009-04-30 14:48:29 +08:00
pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED ;
2006-08-28 11:43:25 -07:00
int retval ;
2006-07-31 15:26:16 +08:00
/* If fatal, restore cfg space for possible link reset at upstream */
if ( dev - > error_state = = pci_channel_io_frozen ) {
2009-09-14 22:25:11 +02:00
dev - > state_saved = true ;
2008-12-27 16:28:58 +01:00
pci_restore_state ( dev ) ;
2006-07-31 15:26:16 +08:00
pcie_portdrv_restore_config ( dev ) ;
pci_enable_pcie_error_reporting ( dev ) ;
}
2006-08-28 11:43:25 -07:00
/* get true return value from &status */
retval = device_for_each_child ( & dev - > dev , & status , slot_reset_iter ) ;
2006-07-31 15:26:16 +08:00
return status ;
}
static int resume_iter ( struct device * device , void * data )
{
struct pcie_device * pcie_device ;
struct pcie_port_service_driver * driver ;
if ( device - > bus = = & pcie_port_bus_type & & device - > driver ) {
driver = to_service_driver ( device - > driver ) ;
if ( driver & &
driver - > err_handler & &
driver - > err_handler - > resume ) {
pcie_device = to_pcie_device ( device ) ;
/* Forward error message to service drivers */
driver - > err_handler - > resume ( pcie_device - > port ) ;
}
}
return 0 ;
}
static void pcie_portdrv_err_resume ( struct pci_dev * dev )
{
2006-08-28 11:43:25 -07:00
int retval ;
/* nothing to do with error value, if it ever happens */
retval = device_for_each_child ( & dev - > dev , NULL , resume_iter ) ;
2005-04-16 15:20:36 -07:00
}
/*
* LINUX Device Driver Model
*/
static const struct pci_device_id port_pci_ids [ ] = { {
/* handle any PCI-Express port */
PCI_DEVICE_CLASS ( ( ( PCI_CLASS_BRIDGE_PCI < < 8 ) | 0x00 ) , ~ 0 ) ,
} , { /* end: all zeroes */ }
} ;
MODULE_DEVICE_TABLE ( pci , port_pci_ids ) ;
2006-07-31 15:26:16 +08:00
static struct pci_error_handlers pcie_portdrv_err_handler = {
. error_detected = pcie_portdrv_error_detected ,
. mmio_enabled = pcie_portdrv_mmio_enabled ,
. slot_reset = pcie_portdrv_slot_reset ,
. resume = pcie_portdrv_err_resume ,
} ;
2007-02-27 10:19:17 +01:00
static struct pci_driver pcie_portdriver = {
2009-10-05 16:47:34 -06:00
. name = " pcieport " ,
2005-04-16 15:20:36 -07:00
. id_table = & port_pci_ids [ 0 ] ,
. probe = pcie_portdrv_probe ,
. remove = pcie_portdrv_remove ,
2006-07-31 15:26:16 +08:00
. err_handler = & pcie_portdrv_err_handler ,
2009-02-15 22:32:48 +01:00
. driver . pm = PCIE_PORTDRV_PM_OPS ,
2005-04-16 15:20:36 -07:00
} ;
static int __init pcie_portdrv_init ( void )
{
2006-07-08 22:58:25 -07:00
int retval ;
2005-04-16 15:20:36 -07:00
2006-07-08 22:58:25 -07:00
retval = pcie_port_bus_register ( ) ;
if ( retval ) {
printk ( KERN_WARNING " PCIE: bus_register error: %d \n " , retval ) ;
goto out ;
}
2007-02-27 10:19:17 +01:00
retval = pci_register_driver ( & pcie_portdriver ) ;
2005-04-16 15:20:36 -07:00
if ( retval )
pcie_port_bus_unregister ( ) ;
2006-07-08 22:58:25 -07:00
out :
2005-04-16 15:20:36 -07:00
return retval ;
}
static void __exit pcie_portdrv_exit ( void )
{
2007-02-27 10:19:17 +01:00
pci_unregister_driver ( & pcie_portdriver ) ;
2005-04-16 15:20:36 -07:00
pcie_port_bus_unregister ( ) ;
}
module_init ( pcie_portdrv_init ) ;
module_exit ( pcie_portdrv_exit ) ;