Skip to content

Commit

Permalink
[PATCH] PCI: altix: msi support
Browse files Browse the repository at this point in the history
MSI callouts for altix.  Involves a fair amount of code reorg in sn irq.c
code as well as adding some extensions to the altix PCI provider abstaction.

Signed-off-by: Mark Maule <maule@sgi.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
Mark Maule authored and Greg Kroah-Hartman committed Jun 21, 2006
1 parent 1008307 commit 83821d3
Show file tree
Hide file tree
Showing 11 changed files with 401 additions and 121 deletions.
9 changes: 1 addition & 8 deletions arch/ia64/sn/kernel/io_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ static int max_pcibus_number = 255; /* Default highest pci bus number */
*/

static dma_addr_t
sn_default_pci_map(struct pci_dev *pdev, unsigned long paddr, size_t size)
sn_default_pci_map(struct pci_dev *pdev, unsigned long paddr, size_t size, int type)
{
return 0;
}
Expand Down Expand Up @@ -457,13 +457,6 @@ void sn_pci_fixup_slot(struct pci_dev *dev)
pcidev_info->pdi_sn_irq_info = NULL;
kfree(sn_irq_info);
}

/*
* MSI currently not supported on altix. Remove this when
* the MSI abstraction patches are integrated into the kernel
* (sometime after 2.6.16 releases)
*/
dev->no_msi = 1;
}

/*
Expand Down
135 changes: 77 additions & 58 deletions arch/ia64/sn/kernel/irq.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ static void unregister_intr_pda(struct sn_irq_info *sn_irq_info);

int sn_force_interrupt_flag = 1;
extern int sn_ioif_inited;
static struct list_head **sn_irq_lh;
struct list_head **sn_irq_lh;
static spinlock_t sn_irq_info_lock = SPIN_LOCK_UNLOCKED; /* non-IRQ lock */

static inline u64 sn_intr_alloc(nasid_t local_nasid, int local_widget,
u64 sn_irq_info,
u64 sn_intr_alloc(nasid_t local_nasid, int local_widget,
struct sn_irq_info *sn_irq_info,
int req_irq, nasid_t req_nasid,
int req_slice)
{
Expand All @@ -40,12 +40,13 @@ static inline u64 sn_intr_alloc(nasid_t local_nasid, int local_widget,

SAL_CALL_NOLOCK(ret_stuff, (u64) SN_SAL_IOIF_INTERRUPT,
(u64) SAL_INTR_ALLOC, (u64) local_nasid,
(u64) local_widget, (u64) sn_irq_info, (u64) req_irq,
(u64) local_widget, __pa(sn_irq_info), (u64) req_irq,
(u64) req_nasid, (u64) req_slice);

return ret_stuff.status;
}

static inline void sn_intr_free(nasid_t local_nasid, int local_widget,
void sn_intr_free(nasid_t local_nasid, int local_widget,
struct sn_irq_info *sn_irq_info)
{
struct ia64_sal_retval ret_stuff;
Expand Down Expand Up @@ -112,73 +113,91 @@ static void sn_end_irq(unsigned int irq)

static void sn_irq_info_free(struct rcu_head *head);

static void sn_set_affinity_irq(unsigned int irq, cpumask_t mask)
struct sn_irq_info *sn_retarget_vector(struct sn_irq_info *sn_irq_info,
nasid_t nasid, int slice)
{
struct sn_irq_info *sn_irq_info, *sn_irq_info_safe;
int cpuid, cpuphys;
int vector;
int cpuphys;
int64_t bridge;
int local_widget, status;
nasid_t local_nasid;
struct sn_irq_info *new_irq_info;
struct sn_pcibus_provider *pci_provider;

cpuid = first_cpu(mask);
cpuphys = cpu_physical_id(cpuid);
new_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_ATOMIC);
if (new_irq_info == NULL)
return NULL;

list_for_each_entry_safe(sn_irq_info, sn_irq_info_safe,
sn_irq_lh[irq], list) {
u64 bridge;
int local_widget, status;
nasid_t local_nasid;
struct sn_irq_info *new_irq_info;
struct sn_pcibus_provider *pci_provider;

new_irq_info = kmalloc(sizeof(struct sn_irq_info), GFP_ATOMIC);
if (new_irq_info == NULL)
break;
memcpy(new_irq_info, sn_irq_info, sizeof(struct sn_irq_info));

bridge = (u64) new_irq_info->irq_bridge;
if (!bridge) {
kfree(new_irq_info);
break; /* irq is not a device interrupt */
}
memcpy(new_irq_info, sn_irq_info, sizeof(struct sn_irq_info));

local_nasid = NASID_GET(bridge);
bridge = (u64) new_irq_info->irq_bridge;
if (!bridge) {
kfree(new_irq_info);
return NULL; /* irq is not a device interrupt */
}

if (local_nasid & 1)
local_widget = TIO_SWIN_WIDGETNUM(bridge);
else
local_widget = SWIN_WIDGETNUM(bridge);
local_nasid = NASID_GET(bridge);

/* Free the old PROM new_irq_info structure */
sn_intr_free(local_nasid, local_widget, new_irq_info);
/* Update kernels new_irq_info with new target info */
unregister_intr_pda(new_irq_info);
if (local_nasid & 1)
local_widget = TIO_SWIN_WIDGETNUM(bridge);
else
local_widget = SWIN_WIDGETNUM(bridge);

/* allocate a new PROM new_irq_info struct */
status = sn_intr_alloc(local_nasid, local_widget,
__pa(new_irq_info), irq,
cpuid_to_nasid(cpuid),
cpuid_to_slice(cpuid));
vector = sn_irq_info->irq_irq;
/* Free the old PROM new_irq_info structure */
sn_intr_free(local_nasid, local_widget, new_irq_info);
/* Update kernels new_irq_info with new target info */
unregister_intr_pda(new_irq_info);

/* SAL call failed */
if (status) {
kfree(new_irq_info);
break;
}
/* allocate a new PROM new_irq_info struct */
status = sn_intr_alloc(local_nasid, local_widget,
new_irq_info, vector,
nasid, slice);

/* SAL call failed */
if (status) {
kfree(new_irq_info);
return NULL;
}

new_irq_info->irq_cpuid = cpuid;
register_intr_pda(new_irq_info);
cpuphys = nasid_slice_to_cpuid(nasid, slice);
new_irq_info->irq_cpuid = cpuphys;
register_intr_pda(new_irq_info);

pci_provider = sn_pci_provider[new_irq_info->irq_bridge_type];
if (pci_provider && pci_provider->target_interrupt)
(pci_provider->target_interrupt)(new_irq_info);
pci_provider = sn_pci_provider[new_irq_info->irq_bridge_type];

/*
* If this represents a line interrupt, target it. If it's
* an msi (irq_int_bit < 0), it's already targeted.
*/
if (new_irq_info->irq_int_bit >= 0 &&
pci_provider && pci_provider->target_interrupt)
(pci_provider->target_interrupt)(new_irq_info);

spin_lock(&sn_irq_info_lock);
list_replace_rcu(&sn_irq_info->list, &new_irq_info->list);
spin_unlock(&sn_irq_info_lock);
call_rcu(&sn_irq_info->rcu, sn_irq_info_free);
spin_lock(&sn_irq_info_lock);
list_replace_rcu(&sn_irq_info->list, &new_irq_info->list);
spin_unlock(&sn_irq_info_lock);
call_rcu(&sn_irq_info->rcu, sn_irq_info_free);

#ifdef CONFIG_SMP
set_irq_affinity_info((irq & 0xff), cpuphys, 0);
set_irq_affinity_info((vector & 0xff), cpuphys, 0);
#endif
}

return new_irq_info;
}

static void sn_set_affinity_irq(unsigned int irq, cpumask_t mask)
{
struct sn_irq_info *sn_irq_info, *sn_irq_info_safe;
nasid_t nasid;
int slice;

nasid = cpuid_to_nasid(first_cpu(mask));
slice = cpuid_to_slice(first_cpu(mask));

list_for_each_entry_safe(sn_irq_info, sn_irq_info_safe,
sn_irq_lh[irq], list)
(void)sn_retarget_vector(sn_irq_info, nasid, slice);
}

struct hw_interrupt_type irq_type_sn = {
Expand Down
10 changes: 6 additions & 4 deletions arch/ia64/sn/pci/pci_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

#include <linux/module.h>
#include <asm/dma.h>
#include <asm/sn/pcibr_provider.h>
#include <asm/sn/intr.h>
#include <asm/sn/pcibus_provider_defs.h>
#include <asm/sn/pcidev.h>
#include <asm/sn/sn_sal.h>
Expand Down Expand Up @@ -113,7 +113,8 @@ void *sn_dma_alloc_coherent(struct device *dev, size_t size,
* resources.
*/

*dma_handle = provider->dma_map_consistent(pdev, phys_addr, size);
*dma_handle = provider->dma_map_consistent(pdev, phys_addr, size,
SN_DMA_ADDR_PHYS);
if (!*dma_handle) {
printk(KERN_ERR "%s: out of ATEs\n", __FUNCTION__);
free_pages((unsigned long)cpuaddr, get_order(size));
Expand Down Expand Up @@ -176,7 +177,7 @@ dma_addr_t sn_dma_map_single(struct device *dev, void *cpu_addr, size_t size,
BUG_ON(dev->bus != &pci_bus_type);

phys_addr = __pa(cpu_addr);
dma_addr = provider->dma_map(pdev, phys_addr, size);
dma_addr = provider->dma_map(pdev, phys_addr, size, SN_DMA_ADDR_PHYS);
if (!dma_addr) {
printk(KERN_ERR "%s: out of ATEs\n", __FUNCTION__);
return 0;
Expand Down Expand Up @@ -260,7 +261,8 @@ int sn_dma_map_sg(struct device *dev, struct scatterlist *sg, int nhwentries,
for (i = 0; i < nhwentries; i++, sg++) {
phys_addr = SG_ENT_PHYS_ADDRESS(sg);
sg->dma_address = provider->dma_map(pdev,
phys_addr, sg->length);
phys_addr, sg->length,
SN_DMA_ADDR_PHYS);

if (!sg->dma_address) {
printk(KERN_ERR "%s: out of ATEs\n", __FUNCTION__);
Expand Down
62 changes: 44 additions & 18 deletions arch/ia64/sn/pci/pcibr/pcibr_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ extern int sn_ioif_inited;

static dma_addr_t
pcibr_dmamap_ate32(struct pcidev_info *info,
u64 paddr, size_t req_size, u64 flags)
u64 paddr, size_t req_size, u64 flags, int dma_flags)
{

struct pcidev_info *pcidev_info = info->pdi_host_pcidev_info;
Expand Down Expand Up @@ -81,16 +81,26 @@ pcibr_dmamap_ate32(struct pcidev_info *info,
if (IS_PCIX(pcibus_info))
ate_flags &= ~(PCI32_ATE_PREF);

xio_addr =
IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) :
PHYS_TO_TIODMA(paddr);
if (SN_DMA_ADDRTYPE(dma_flags == SN_DMA_ADDR_PHYS))
xio_addr = IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) :
PHYS_TO_TIODMA(paddr);
else
xio_addr = paddr;

offset = IOPGOFF(xio_addr);
ate = ate_flags | (xio_addr - offset);

/* If PIC, put the targetid in the ATE */
if (IS_PIC_SOFT(pcibus_info)) {
ate |= (pcibus_info->pbi_hub_xid << PIC_ATE_TARGETID_SHFT);
}

/*
* If we're mapping for MSI, set the MSI bit in the ATE
*/
if (dma_flags & SN_DMA_MSI)
ate |= PCI32_ATE_MSI;

ate_write(pcibus_info, ate_index, ate_count, ate);

/*
Expand All @@ -105,20 +115,27 @@ pcibr_dmamap_ate32(struct pcidev_info *info,
if (pcibus_info->pbi_devreg[internal_device] & PCIBR_DEV_SWAP_DIR)
ATE_SWAP_ON(pci_addr);


return pci_addr;
}

static dma_addr_t
pcibr_dmatrans_direct64(struct pcidev_info * info, u64 paddr,
u64 dma_attributes)
u64 dma_attributes, int dma_flags)
{
struct pcibus_info *pcibus_info = (struct pcibus_info *)
((info->pdi_host_pcidev_info)->pdi_pcibus_info);
u64 pci_addr;

/* Translate to Crosstalk View of Physical Address */
pci_addr = (IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) :
PHYS_TO_TIODMA(paddr)) | dma_attributes;
if (SN_DMA_ADDRTYPE(dma_flags) == SN_DMA_ADDR_PHYS)
pci_addr = IS_PIC_SOFT(pcibus_info) ?
PHYS_TO_DMA(paddr) :
PHYS_TO_TIODMA(paddr) | dma_attributes;
else
pci_addr = IS_PIC_SOFT(pcibus_info) ?
paddr :
paddr | dma_attributes;

/* Handle Bus mode */
if (IS_PCIX(pcibus_info))
Expand All @@ -130,7 +147,9 @@ pcibr_dmatrans_direct64(struct pcidev_info * info, u64 paddr,
((u64) pcibus_info->
pbi_hub_xid << PIC_PCI64_ATTR_TARG_SHFT);
} else
pci_addr |= TIOCP_PCI64_CMDTYPE_MEM;
pci_addr |= (dma_flags & SN_DMA_MSI) ?
TIOCP_PCI64_CMDTYPE_MSI :
TIOCP_PCI64_CMDTYPE_MEM;

/* If PCI mode, func zero uses VCHAN0, every other func uses VCHAN1 */
if (!IS_PCIX(pcibus_info) && PCI_FUNC(info->pdi_linux_pcidev->devfn))
Expand All @@ -141,7 +160,7 @@ pcibr_dmatrans_direct64(struct pcidev_info * info, u64 paddr,

static dma_addr_t
pcibr_dmatrans_direct32(struct pcidev_info * info,
u64 paddr, size_t req_size, u64 flags)
u64 paddr, size_t req_size, u64 flags, int dma_flags)
{
struct pcidev_info *pcidev_info = info->pdi_host_pcidev_info;
struct pcibus_info *pcibus_info = (struct pcibus_info *)pcidev_info->
Expand All @@ -156,8 +175,14 @@ pcibr_dmatrans_direct32(struct pcidev_info * info,
return 0;
}

xio_addr = IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) :
PHYS_TO_TIODMA(paddr);
if (dma_flags & SN_DMA_MSI)
return 0;

if (SN_DMA_ADDRTYPE(dma_flags) == SN_DMA_ADDR_PHYS)
xio_addr = IS_PIC_SOFT(pcibus_info) ? PHYS_TO_DMA(paddr) :
PHYS_TO_TIODMA(paddr);
else
xio_addr = paddr;

xio_base = pcibus_info->pbi_dir_xbase;
offset = xio_addr - xio_base;
Expand Down Expand Up @@ -327,7 +352,7 @@ void sn_dma_flush(u64 addr)
*/

dma_addr_t
pcibr_dma_map(struct pci_dev * hwdev, unsigned long phys_addr, size_t size)
pcibr_dma_map(struct pci_dev * hwdev, unsigned long phys_addr, size_t size, int dma_flags)
{
dma_addr_t dma_handle;
struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev);
Expand All @@ -344,19 +369,20 @@ pcibr_dma_map(struct pci_dev * hwdev, unsigned long phys_addr, size_t size)
*/

dma_handle = pcibr_dmatrans_direct64(pcidev_info, phys_addr,
PCI64_ATTR_PREF);
PCI64_ATTR_PREF, dma_flags);
} else {
/* Handle 32-63 bit cards via direct mapping */
dma_handle = pcibr_dmatrans_direct32(pcidev_info, phys_addr,
size, 0);
size, 0, dma_flags);
if (!dma_handle) {
/*
* It is a 32 bit card and we cannot do direct mapping,
* so we use an ATE.
*/

dma_handle = pcibr_dmamap_ate32(pcidev_info, phys_addr,
size, PCI32_ATE_PREF);
size, PCI32_ATE_PREF,
dma_flags);
}
}

Expand All @@ -365,18 +391,18 @@ pcibr_dma_map(struct pci_dev * hwdev, unsigned long phys_addr, size_t size)

dma_addr_t
pcibr_dma_map_consistent(struct pci_dev * hwdev, unsigned long phys_addr,
size_t size)
size_t size, int dma_flags)
{
dma_addr_t dma_handle;
struct pcidev_info *pcidev_info = SN_PCIDEV_INFO(hwdev);

if (hwdev->dev.coherent_dma_mask == ~0UL) {
dma_handle = pcibr_dmatrans_direct64(pcidev_info, phys_addr,
PCI64_ATTR_BAR);
PCI64_ATTR_BAR, dma_flags);
} else {
dma_handle = (dma_addr_t) pcibr_dmamap_ate32(pcidev_info,
phys_addr, size,
PCI32_ATE_BAR);
PCI32_ATE_BAR, dma_flags);
}

return dma_handle;
Expand Down
Loading

0 comments on commit 83821d3

Please sign in to comment.