2006-10-04 13:16:55 +04:00
/*
* File : htirq . c
* Purpose : Hypertransport Interrupt Capability
*
* Copyright ( C ) 2006 Linux Networx
* Copyright ( C ) Eric Biederman < ebiederman @ lnxi . com >
*/
# include <linux/irq.h>
# include <linux/pci.h>
# include <linux/spinlock.h>
2011-05-27 17:37:25 +04:00
# include <linux/export.h>
2006-10-04 13:16:55 +04:00
# include <linux/slab.h>
2006-10-04 13:17:01 +04:00
# include <linux/htirq.h>
2006-10-04 13:16:55 +04:00
/* Global ht irq lock.
*
* This is needed to serialize access to the data port in hypertransport
* irq capability .
*
* With multiple simultaneous hypertransport irq devices it might pay
* to make this more fine grained . But start with simple , stupid , and correct .
*/
static DEFINE_SPINLOCK ( ht_irq_lock ) ;
2006-11-09 04:44:57 +03:00
void write_ht_irq_msg ( unsigned int irq , struct ht_irq_msg * msg )
2006-10-04 13:16:55 +04:00
{
2011-03-28 19:49:12 +04:00
struct ht_irq_cfg * cfg = irq_get_handler_data ( irq ) ;
2006-10-04 13:16:55 +04:00
unsigned long flags ;
2015-04-13 09:11:43 +03:00
2006-10-04 13:16:55 +04:00
spin_lock_irqsave ( & ht_irq_lock , flags ) ;
2006-11-09 04:44:57 +03:00
if ( cfg - > msg . address_lo ! = msg - > address_lo ) {
pci_write_config_byte ( cfg - > dev , cfg - > pos + 2 , cfg - > idx ) ;
pci_write_config_dword ( cfg - > dev , cfg - > pos + 4 , msg - > address_lo ) ;
}
if ( cfg - > msg . address_hi ! = msg - > address_hi ) {
pci_write_config_byte ( cfg - > dev , cfg - > pos + 2 , cfg - > idx + 1 ) ;
pci_write_config_dword ( cfg - > dev , cfg - > pos + 4 , msg - > address_hi ) ;
}
2006-11-09 04:44:57 +03:00
if ( cfg - > update )
cfg - > update ( cfg - > dev , irq , msg ) ;
2006-10-04 13:16:55 +04:00
spin_unlock_irqrestore ( & ht_irq_lock , flags ) ;
2006-11-09 04:44:57 +03:00
cfg - > msg = * msg ;
2006-10-04 13:16:55 +04:00
}
2006-11-09 04:44:57 +03:00
void fetch_ht_irq_msg ( unsigned int irq , struct ht_irq_msg * msg )
2006-10-04 13:16:55 +04:00
{
2011-03-28 19:49:12 +04:00
struct ht_irq_cfg * cfg = irq_get_handler_data ( irq ) ;
2015-04-13 09:11:43 +03:00
2006-11-09 04:44:57 +03:00
* msg = cfg - > msg ;
2006-10-04 13:16:55 +04:00
}
2010-09-28 19:22:09 +04:00
void mask_ht_irq ( struct irq_data * data )
2006-10-04 13:16:55 +04:00
{
2011-03-28 19:49:12 +04:00
struct ht_irq_cfg * cfg = irq_data_get_irq_handler_data ( data ) ;
2010-09-28 19:22:09 +04:00
struct ht_irq_msg msg = cfg - > msg ;
2006-10-04 13:16:55 +04:00
2006-11-09 04:44:57 +03:00
msg . address_lo | = 1 ;
2010-09-28 19:22:09 +04:00
write_ht_irq_msg ( data - > irq , & msg ) ;
2006-10-04 13:16:55 +04:00
}
2010-09-28 19:22:09 +04:00
void unmask_ht_irq ( struct irq_data * data )
2006-10-04 13:16:55 +04:00
{
2011-03-28 19:49:12 +04:00
struct ht_irq_cfg * cfg = irq_data_get_irq_handler_data ( data ) ;
2010-09-28 19:22:09 +04:00
struct ht_irq_msg msg = cfg - > msg ;
2006-10-04 13:16:55 +04:00
2006-11-09 04:44:57 +03:00
msg . address_lo & = ~ 1 ;
2010-09-28 19:22:09 +04:00
write_ht_irq_msg ( data - > irq , & msg ) ;
2006-10-04 13:16:55 +04:00
}
/**
2006-11-09 04:44:57 +03:00
* __ht_create_irq - create an irq and attach it to a device .
2006-10-04 13:16:55 +04:00
* @ dev : The hypertransport device to find the irq capability on .
* @ idx : Which of the possible irqs to attach to .
2006-11-09 04:44:57 +03:00
* @ update : Function to be called when changing the htirq message
2006-10-04 13:16:55 +04:00
*
* The irq number of the new irq or a negative error value is returned .
*/
2006-11-09 04:44:57 +03:00
int __ht_create_irq ( struct pci_dev * dev , int idx , ht_irq_update_t * update )
2006-10-04 13:16:55 +04:00
{
2014-05-07 19:44:08 +04:00
int max_irq , pos , irq ;
2006-10-04 13:16:55 +04:00
unsigned long flags ;
u32 data ;
2006-11-22 10:26:19 +03:00
pos = pci_find_ht_capability ( dev , HT_CAPTYPE_IRQ ) ;
2006-10-04 13:16:55 +04:00
if ( ! pos )
return - EINVAL ;
/* Verify the idx I want to use is in range */
spin_lock_irqsave ( & ht_irq_lock , flags ) ;
pci_write_config_byte ( dev , pos + 2 , 1 ) ;
pci_read_config_dword ( dev , pos + 4 , & data ) ;
spin_unlock_irqrestore ( & ht_irq_lock , flags ) ;
max_irq = ( data > > 16 ) & 0xff ;
2014-04-19 04:13:49 +04:00
if ( idx > max_irq )
2006-10-04 13:16:55 +04:00
return - EINVAL ;
2015-04-13 09:11:43 +03:00
irq = arch_setup_ht_irq ( idx , pos , dev , update ) ;
if ( irq > 0 )
dev_dbg ( & dev - > dev , " irq %d for HT \n " , irq ) ;
2006-10-04 13:16:55 +04:00
return irq ;
}
2014-04-26 00:32:25 +04:00
EXPORT_SYMBOL ( __ht_create_irq ) ;
2006-10-04 13:16:55 +04:00
2006-11-09 04:44:57 +03:00
/**
* ht_create_irq - create an irq and attach it to a device .
* @ dev : The hypertransport device to find the irq capability on .
* @ idx : Which of the possible irqs to attach to .
*
* ht_create_irq needs to be called for all hypertransport devices
* that generate irqs .
*
* The irq number of the new irq or a negative error value is returned .
*/
int ht_create_irq ( struct pci_dev * dev , int idx )
{
return __ht_create_irq ( dev , idx , NULL ) ;
}
2014-04-26 00:32:25 +04:00
EXPORT_SYMBOL ( ht_create_irq ) ;
2006-11-09 04:44:57 +03:00
2006-10-04 13:16:55 +04:00
/**
* ht_destroy_irq - destroy an irq created with ht_create_irq
2009-04-11 02:17:50 +04:00
* @ irq : irq to be destroyed
2006-10-04 13:16:55 +04:00
*
* This reverses ht_create_irq removing the specified irq from
* existence . The irq should be free before this happens .
*/
void ht_destroy_irq ( unsigned int irq )
{
2015-04-13 09:11:43 +03:00
arch_teardown_ht_irq ( irq ) ;
2006-10-04 13:16:55 +04:00
}
EXPORT_SYMBOL ( ht_destroy_irq ) ;