msi_sn.c 5.8 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * This file is subject to the terms and conditions of the GNU General Public
 * License.  See the file "COPYING" in the main directory of this archive
 * for more details.
 *
 * Copyright (C) 2006 Silicon Graphics, Inc.  All Rights Reserved.
 */

M
Mark Maule 已提交
9
#include <linux/types.h>
10
#include <linux/irq.h>
M
Mark Maule 已提交
11 12
#include <linux/pci.h>
#include <linux/cpumask.h>
13
#include <linux/msi.h>
14
#include <linux/slab.h>
M
Mark Maule 已提交
15 16 17 18 19 20 21 22 23 24 25 26

#include <asm/sn/addrs.h>
#include <asm/sn/intr.h>
#include <asm/sn/pcibus_provider_defs.h>
#include <asm/sn/pcidev.h>
#include <asm/sn/nodepda.h>

struct sn_msi_info {
	u64 pci_addr;
	struct sn_irq_info *sn_irq_info;
};

27 28 29
static struct sn_msi_info sn_msi_info[NR_IRQS];

static struct irq_chip sn_msi_chip;
M
Mark Maule 已提交
30

31
void sn_teardown_msi_irq(unsigned int irq)
M
Mark Maule 已提交
32 33 34 35 36 37 38 39 40
{
	nasid_t nasid;
	int widget;
	struct pci_dev *pdev;
	struct pcidev_info *sn_pdev;
	struct sn_irq_info *sn_irq_info;
	struct pcibus_bussoft *bussoft;
	struct sn_pcibus_provider *provider;

41
	sn_irq_info = sn_msi_info[irq].sn_irq_info;
M
Mark Maule 已提交
42 43 44 45 46 47 48 49
	if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0)
		return;

	sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
	pdev = sn_pdev->pdi_linux_pcidev;
	provider = SN_PCIDEV_BUSPROVIDER(pdev);

	(*provider->dma_unmap)(pdev,
50
			       sn_msi_info[irq].pci_addr,
M
Mark Maule 已提交
51
			       PCI_DMA_FROMDEVICE);
52
	sn_msi_info[irq].pci_addr = 0;
M
Mark Maule 已提交
53 54 55 56 57 58 59 60

	bussoft = SN_PCIDEV_BUSSOFT(pdev);
	nasid = NASID_GET(bussoft->bs_base);
	widget = (nasid & 1) ?
			TIO_SWIN_WIDGETNUM(bussoft->bs_base) :
			SWIN_WIDGETNUM(bussoft->bs_base);

	sn_intr_free(nasid, widget, sn_irq_info);
61
	sn_msi_info[irq].sn_irq_info = NULL;
M
Mark Maule 已提交
62

63
	destroy_irq(irq);
M
Mark Maule 已提交
64
}
65

66
int sn_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *entry)
67
{
68
	struct msi_msg msg;
M
Mark Maule 已提交
69 70 71 72 73 74 75
	int widget;
	int status;
	nasid_t nasid;
	u64 bus_addr;
	struct sn_irq_info *sn_irq_info;
	struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(pdev);
	struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev);
76
	int irq;
M
Mark Maule 已提交
77

78 79 80
	if (!entry->msi_attrib.is_64)
		return -EINVAL;

M
Mark Maule 已提交
81 82 83 84 85 86
	if (bussoft == NULL)
		return -EINVAL;

	if (provider == NULL || provider->dma_map_consistent == NULL)
		return -EINVAL;

87 88 89 90
	irq = create_irq();
	if (irq < 0)
		return irq;

M
Mark Maule 已提交
91 92 93 94 95 96 97 98 99 100 101
	/*
	 * Set up the vector plumbing.  Let the prom (via sn_intr_alloc)
	 * decide which cpu to direct this msi at by default.
	 */

	nasid = NASID_GET(bussoft->bs_base);
	widget = (nasid & 1) ?
			TIO_SWIN_WIDGETNUM(bussoft->bs_base) :
			SWIN_WIDGETNUM(bussoft->bs_base);

	sn_irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL);
102 103
	if (! sn_irq_info) {
		destroy_irq(irq);
M
Mark Maule 已提交
104
		return -ENOMEM;
105
	}
M
Mark Maule 已提交
106

107
	status = sn_intr_alloc(nasid, widget, sn_irq_info, irq, -1, -1);
M
Mark Maule 已提交
108 109
	if (status) {
		kfree(sn_irq_info);
110
		destroy_irq(irq);
M
Mark Maule 已提交
111 112 113 114 115 116 117 118 119 120
		return -ENOMEM;
	}

	sn_irq_info->irq_int_bit = -1;		/* mark this as an MSI irq */
	sn_irq_fixup(pdev, sn_irq_info);

	/* Prom probably should fill these in, but doesn't ... */
	sn_irq_info->irq_bridge_type = bussoft->bs_asic_type;
	sn_irq_info->irq_bridge = (void *)bussoft->bs_base;

121
	/*
M
Mark Maule 已提交
122
	 * Map the xio address into bus space
123
	 */
M
Mark Maule 已提交
124 125 126 127 128 129 130
	bus_addr = (*provider->dma_map_consistent)(pdev,
					sn_irq_info->irq_xtalkaddr,
					sizeof(sn_irq_info->irq_xtalkaddr),
					SN_DMA_MSI|SN_DMA_ADDR_XIO);
	if (! bus_addr) {
		sn_intr_free(nasid, widget, sn_irq_info);
		kfree(sn_irq_info);
131
		destroy_irq(irq);
M
Mark Maule 已提交
132 133 134
		return -ENOMEM;
	}

135 136
	sn_msi_info[irq].sn_irq_info = sn_irq_info;
	sn_msi_info[irq].pci_addr = bus_addr;
M
Mark Maule 已提交
137

138 139
	msg.address_hi = (u32)(bus_addr >> 32);
	msg.address_lo = (u32)(bus_addr & 0x00000000ffffffff);
M
Mark Maule 已提交
140 141 142 143 144

	/*
	 * In the SN platform, bit 16 is a "send vector" bit which
	 * must be present in order to move the vector through the system.
	 */
145
	msg.data = 0x100 + irq;
M
Mark Maule 已提交
146

147
	irq_set_msi_desc(irq, entry);
148
	pci_write_msi_msg(irq, &msg);
149
	irq_set_chip_and_handler(irq, &sn_msi_chip, handle_edge_irq);
150

151
	return 0;
M
Mark Maule 已提交
152 153
}

154
#ifdef CONFIG_SMP
155 156
static int sn_set_msi_irq_affinity(struct irq_data *data,
				   const struct cpumask *cpu_mask, bool force)
M
Mark Maule 已提交
157
{
158
	struct msi_msg msg;
M
Mark Maule 已提交
159 160 161 162 163 164 165 166
	int slice;
	nasid_t nasid;
	u64 bus_addr;
	struct pci_dev *pdev;
	struct pcidev_info *sn_pdev;
	struct sn_irq_info *sn_irq_info;
	struct sn_irq_info *new_irq_info;
	struct sn_pcibus_provider *provider;
167
	unsigned int cpu, irq = data->irq;
M
Mark Maule 已提交
168

169
	cpu = cpumask_first_and(cpu_mask, cpu_online_mask);
170
	sn_irq_info = sn_msi_info[irq].sn_irq_info;
M
Mark Maule 已提交
171
	if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0)
172
		return -1;
M
Mark Maule 已提交
173 174 175 176 177

	/*
	 * Release XIO resources for the old MSI PCI address
	 */

178
	__get_cached_msi_msg(irq_data_get_msi_desc(data), &msg);
179
	sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
M
Mark Maule 已提交
180 181 182
	pdev = sn_pdev->pdi_linux_pcidev;
	provider = SN_PCIDEV_BUSPROVIDER(pdev);

183
	bus_addr = (u64)(msg.address_hi) << 32 | (u64)(msg.address_lo);
M
Mark Maule 已提交
184
	(*provider->dma_unmap)(pdev, bus_addr, PCI_DMA_FROMDEVICE);
185
	sn_msi_info[irq].pci_addr = 0;
M
Mark Maule 已提交
186 187 188 189 190

	nasid = cpuid_to_nasid(cpu);
	slice = cpuid_to_slice(cpu);

	new_irq_info = sn_retarget_vector(sn_irq_info, nasid, slice);
191
	sn_msi_info[irq].sn_irq_info = new_irq_info;
M
Mark Maule 已提交
192
	if (new_irq_info == NULL)
193
		return -1;
M
Mark Maule 已提交
194 195 196 197 198 199 200 201 202 203

	/*
	 * Map the xio address into bus space
	 */

	bus_addr = (*provider->dma_map_consistent)(pdev,
					new_irq_info->irq_xtalkaddr,
					sizeof(new_irq_info->irq_xtalkaddr),
					SN_DMA_MSI|SN_DMA_ADDR_XIO);

204
	sn_msi_info[irq].pci_addr = bus_addr;
205 206 207
	msg.address_hi = (u32)(bus_addr >> 32);
	msg.address_lo = (u32)(bus_addr & 0x00000000ffffffff);

208
	pci_write_msi_msg(irq, &msg);
209
	cpumask_copy(irq_data_get_affinity_mask(data), cpu_mask);
210 211

	return 0;
M
Mark Maule 已提交
212
}
213
#endif /* CONFIG_SMP */
M
Mark Maule 已提交
214

215
static void sn_ack_msi_irq(struct irq_data *data)
216
{
T
Thomas Gleixner 已提交
217
	irq_move_irq(data);
218 219
	ia64_eoi();
}
M
Mark Maule 已提交
220

221
static int sn_msi_retrigger_irq(struct irq_data *data)
M
Mark Maule 已提交
222
{
223
	unsigned int vector = data->irq;
224
	ia64_resend_irq(vector);
M
Mark Maule 已提交
225

226
	return 1;
227
}
228 229

static struct irq_chip sn_msi_chip = {
230
	.name			= "PCI-MSI",
231 232
	.irq_mask		= pci_msi_mask_irq,
	.irq_unmask		= pci_msi_unmask_irq,
233
	.irq_ack		= sn_ack_msi_irq,
234
#ifdef CONFIG_SMP
235
	.irq_set_affinity	= sn_set_msi_irq_affinity,
236
#endif
237
	.irq_retrigger		= sn_msi_retrigger_irq,
238
};