2019-05-30 02:57:35 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2010-05-17 20:08:21 +04:00
/******************************************************************************
* platform - pci . c
*
* Xen platform PCI device driver
2016-02-22 03:06:08 +03:00
*
* Authors : ssmith @ xensource . com and stefano . stabellini @ eu . citrix . com
*
2010-05-17 20:08:21 +04:00
* Copyright ( c ) 2005 , Intel Corporation .
* Copyright ( c ) 2007 , XenSource Inc .
* Copyright ( c ) 2010 , Citrix
*/
# include <linux/interrupt.h>
# include <linux/io.h>
2016-02-22 03:06:08 +03:00
# include <linux/init.h>
2010-05-17 20:08:21 +04:00
# include <linux/pci.h>
2010-05-14 15:44:30 +04:00
# include <xen/platform_pci.h>
2010-05-17 20:08:21 +04:00
# include <xen/grant_table.h>
# include <xen/xenbus.h>
# include <xen/events.h>
# include <xen/hvm.h>
2010-05-14 15:45:07 +04:00
# include <xen/xen-ops.h>
2010-05-17 20:08:21 +04:00
# define DRV_NAME "xen-platform-pci"
static unsigned long platform_mmio ;
static unsigned long platform_mmio_alloc ;
static unsigned long platform_mmiolen ;
2017-01-13 21:07:23 +03:00
static uint64_t callback_via ;
2010-05-17 20:08:21 +04:00
2014-02-09 14:59:30 +04:00
static unsigned long alloc_xen_mmio ( unsigned long len )
2010-05-17 20:08:21 +04:00
{
unsigned long addr ;
addr = platform_mmio + platform_mmio_alloc ;
platform_mmio_alloc + = len ;
BUG_ON ( platform_mmio_alloc > platform_mmiolen ) ;
return addr ;
}
2017-01-13 21:07:23 +03:00
static uint64_t get_callback_via ( struct pci_dev * pdev )
{
u8 pin ;
int irq ;
irq = pdev - > irq ;
if ( irq < 16 )
return irq ; /* ISA IRQ */
pin = pdev - > pin ;
/* We don't know the GSI. Specify the PCI INTx line instead. */
return ( ( uint64_t ) 0x01 < < HVM_CALLBACK_VIA_TYPE_SHIFT ) | /* PCI INTx identifier */
( ( uint64_t ) pci_domain_nr ( pdev - > bus ) < < 32 ) |
( ( uint64_t ) pdev - > bus - > number < < 16 ) |
( ( uint64_t ) ( pdev - > devfn & 0xff ) < < 8 ) |
( ( uint64_t ) ( pin - 1 ) & 3 ) ;
}
static irqreturn_t do_hvm_evtchn_intr ( int irq , void * dev_id )
{
xen_hvm_evtchn_do_upcall ( ) ;
return IRQ_HANDLED ;
}
static int xen_allocate_irq ( struct pci_dev * pdev )
{
return request_irq ( pdev - > irq , do_hvm_evtchn_intr ,
IRQF_NOBALANCING | IRQF_TRIGGER_RISING ,
" xen-platform-pci " , pdev ) ;
}
static int platform_pci_resume ( struct pci_dev * pdev )
{
int err ;
2017-04-24 22:04:53 +03:00
if ( xen_have_vector_callback )
2017-01-13 21:07:23 +03:00
return 0 ;
2017-04-24 22:04:53 +03:00
2017-01-13 21:07:23 +03:00
err = xen_set_callback_via ( callback_via ) ;
if ( err ) {
dev_err ( & pdev - > dev , " platform_pci_resume failure! \n " ) ;
return err ;
}
return 0 ;
}
2016-02-22 03:06:08 +03:00
static int platform_pci_probe ( struct pci_dev * pdev ,
const struct pci_device_id * ent )
2010-05-17 20:08:21 +04:00
{
int i , ret ;
2011-01-11 14:50:28 +03:00
long ioaddr ;
2010-05-17 20:08:21 +04:00
long mmio_addr , mmio_len ;
unsigned int max_nr_gframes ;
2014-01-06 19:40:36 +04:00
unsigned long grant_frames ;
2010-05-17 20:08:21 +04:00
2012-07-10 17:31:39 +04:00
if ( ! xen_domain ( ) )
return - ENODEV ;
2010-05-17 20:08:21 +04:00
i = pci_enable_device ( pdev ) ;
if ( i )
return i ;
ioaddr = pci_resource_start ( pdev , 0 ) ;
mmio_addr = pci_resource_start ( pdev , 1 ) ;
mmio_len = pci_resource_len ( pdev , 1 ) ;
if ( mmio_addr = = 0 | | ioaddr = = 0 ) {
dev_err ( & pdev - > dev , " no resources found \n " ) ;
ret = - ENOENT ;
goto pci_out ;
}
2011-01-11 14:50:28 +03:00
ret = pci_request_region ( pdev , 1 , DRV_NAME ) ;
if ( ret < 0 )
2010-05-17 20:08:21 +04:00
goto pci_out ;
2011-01-11 14:50:28 +03:00
ret = pci_request_region ( pdev , 0 , DRV_NAME ) ;
if ( ret < 0 )
2010-05-17 20:08:21 +04:00
goto mem_out ;
platform_mmio = mmio_addr ;
platform_mmiolen = mmio_len ;
2017-04-24 22:04:53 +03:00
if ( ! xen_have_vector_callback ) {
2017-01-13 21:07:23 +03:00
ret = xen_allocate_irq ( pdev ) ;
if ( ret ) {
dev_warn ( & pdev - > dev , " request_irq failed err=%d \n " , ret ) ;
goto out ;
}
callback_via = get_callback_via ( pdev ) ;
ret = xen_set_callback_via ( callback_via ) ;
if ( ret ) {
dev_warn ( & pdev - > dev , " Unable to set the evtchn callback "
" err=%d \n " , ret ) ;
goto out ;
}
}
2010-05-17 20:08:21 +04:00
max_nr_gframes = gnttab_max_grant_frames ( ) ;
2014-01-06 19:40:36 +04:00
grant_frames = alloc_xen_mmio ( PAGE_SIZE * max_nr_gframes ) ;
2014-01-07 17:11:05 +04:00
ret = gnttab_setup_auto_xlat_frames ( grant_frames ) ;
if ( ret )
2014-01-06 19:40:36 +04:00
goto out ;
2010-05-17 20:08:21 +04:00
ret = gnttab_init ( ) ;
if ( ret )
2014-01-06 19:40:36 +04:00
goto grant_out ;
2010-05-17 20:08:21 +04:00
xenbus_probe ( NULL ) ;
return 0 ;
2014-01-06 19:40:36 +04:00
grant_out :
gnttab_free_auto_xlat_frames ( ) ;
2010-05-17 20:08:21 +04:00
out :
2011-01-11 14:50:28 +03:00
pci_release_region ( pdev , 0 ) ;
2010-05-17 20:08:21 +04:00
mem_out :
2011-01-11 14:50:28 +03:00
pci_release_region ( pdev , 1 ) ;
2010-05-17 20:08:21 +04:00
pci_out :
pci_disable_device ( pdev ) ;
return ret ;
}
2017-08-02 20:46:57 +03:00
static const struct pci_device_id platform_pci_tbl [ ] = {
2010-05-17 20:08:21 +04:00
{ PCI_VENDOR_ID_XEN , PCI_DEVICE_ID_XEN_PLATFORM ,
PCI_ANY_ID , PCI_ANY_ID , 0 , 0 , 0 } ,
{ 0 , }
} ;
static struct pci_driver platform_driver = {
. name = DRV_NAME ,
2016-02-22 03:06:08 +03:00
. probe = platform_pci_probe ,
2010-05-17 20:08:21 +04:00
. id_table = platform_pci_tbl ,
2017-01-13 21:07:23 +03:00
# ifdef CONFIG_PM
. resume_early = platform_pci_resume ,
# endif
2010-05-17 20:08:21 +04:00
} ;
2016-11-14 15:52:26 +03:00
builtin_pci_driver ( platform_driver ) ;