Commit dc4f60c2 authored by Geoff Levand's avatar Geoff Levand Committed by Paul Mackerras
Browse files

[POWERPC] PS3: Interrupt routine fixups.



Fixups for the ps3 interrupt routines to support all HV device
in a generic way.

Signed-off-by: default avatarGeoff Levand <geoffrey.levand@am.sony.com>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent 12828856
Loading
Loading
Loading
Loading
+166 −68
Original line number Diff line number Diff line
@@ -89,7 +89,18 @@ struct ps3_private {

static DEFINE_PER_CPU(struct ps3_private, ps3_private);

int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet,
/**
 * ps3_virq_setup - virq related setup.
 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
 * serviced on.
 * @outlet: The HV outlet from the various create outlet routines.
 * @virq: The assigned Linux virq.
 *
 * Calls irq_create_mapping() to get a virq and sets the chip data to
 * ps3_private data.
 */

int ps3_virq_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
	unsigned int *virq)
{
	int result;
@@ -111,17 +122,6 @@ int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet,
		goto fail_create;
	}

	/* Binds outlet to cpu + virq. */

	result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0);

	if (result) {
		pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n",
		__func__, __LINE__, ps3_result(result));
		result = -EPERM;
		goto fail_connect;
	}

	pr_debug("%s:%d: outlet %lu => cpu %u, virq %u\n", __func__, __LINE__,
		outlet, cpu, *virq);

@@ -136,94 +136,118 @@ int ps3_alloc_irq(enum ps3_cpu_binding cpu, unsigned long outlet,
	return result;

fail_set:
	lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, *virq);
fail_connect:
	irq_dispose_mapping(*virq);
fail_create:
	return result;
}
EXPORT_SYMBOL_GPL(ps3_alloc_irq);

int ps3_free_irq(unsigned int virq)
/**
 * ps3_virq_destroy - virq related teardown.
 * @virq: The assigned Linux virq.
 *
 * Clears chip data and calls irq_dispose_mapping() for the virq.
 */

int ps3_virq_destroy(unsigned int virq)
{
	int result;
	const struct ps3_private *pd = get_irq_chip_data(virq);

	pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__,
		pd->node, pd->cpu, virq);

	result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq);

	if (result)
		pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n",
		__func__, __LINE__, ps3_result(result));

	set_irq_chip_data(virq, NULL);
	irq_dispose_mapping(virq);
	return result;

	pr_debug("%s:%d <-\n", __func__, __LINE__);
	return 0;
}
EXPORT_SYMBOL_GPL(ps3_free_irq);

/**
 * ps3_alloc_io_irq - Assign a virq to a system bus device.
 * ps3_irq_plug_setup - Generic outlet and virq related setup.
 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
 * serviced on.
 * @interrupt_id: The device interrupt id read from the system repository.
 * @outlet: The HV outlet from the various create outlet routines.
 * @virq: The assigned Linux virq.
 *
 * An io irq represents a non-virtualized device interrupt.  interrupt_id
 * coresponds to the interrupt number of the interrupt controller.
 * Sets up virq and connects the irq plug.
 */

int ps3_alloc_io_irq(enum ps3_cpu_binding cpu, unsigned int interrupt_id,
int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet,
	unsigned int *virq)
{
	int result;
	unsigned long outlet;
	struct ps3_private *pd;

	result = lv1_construct_io_irq_outlet(interrupt_id, &outlet);
	result = ps3_virq_setup(cpu, outlet, virq);

	if (result) {
		pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n",
		pr_debug("%s:%d: ps3_virq_setup failed\n", __func__, __LINE__);
		goto fail_setup;
	}

	pd = get_irq_chip_data(*virq);

	/* Binds outlet to cpu + virq. */

	result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, *virq, outlet, 0);

	if (result) {
		pr_info("%s:%d: lv1_connect_irq_plug_ext failed: %s\n",
		__func__, __LINE__, ps3_result(result));
		return result;
		result = -EPERM;
		goto fail_connect;
	}

	result = ps3_alloc_irq(cpu, outlet, virq);
	BUG_ON(result);
	return result;

fail_connect:
	ps3_virq_destroy(*virq);
fail_setup:
	return result;
}
EXPORT_SYMBOL_GPL(ps3_alloc_io_irq);
EXPORT_SYMBOL_GPL(ps3_irq_plug_setup);

int ps3_free_io_irq(unsigned int virq)
/**
 * ps3_irq_plug_destroy - Generic outlet and virq related teardown.
 * @virq: The assigned Linux virq.
 *
 * Disconnects the irq plug and tears down virq.
 * Do not call for system bus event interrupts setup with
 * ps3_sb_event_receive_port_setup().
 */

int ps3_irq_plug_destroy(unsigned int virq)
{
	int result;
	const struct ps3_private *pd = get_irq_chip_data(virq);

	result = lv1_destruct_io_irq_outlet(virq_to_hw(virq));
	pr_debug("%s:%d: node %lu, cpu %d, virq %u\n", __func__, __LINE__,
		pd->node, pd->cpu, virq);

	result = lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq);

	if (result)
		pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
		pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n",
		__func__, __LINE__, ps3_result(result));

	ps3_free_irq(virq);
	ps3_virq_destroy(virq);

	return result;
}
EXPORT_SYMBOL_GPL(ps3_free_io_irq);
EXPORT_SYMBOL_GPL(ps3_irq_plug_destroy);

/**
 * ps3_alloc_event_irq - Allocate a virq for use with a system event.
 * ps3_event_receive_port_setup - Setup an event receive port.
 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
 * serviced on.
 * @virq: The assigned Linux virq.
 *
 * The virq can be used with lv1_connect_interrupt_event_receive_port() to
 * arrange to receive events, or with ps3_send_event_locally() to signal
 * events.
 * arrange to receive interrupts from system-bus devices, or with
 * ps3_send_event_locally() to signal events.
 */

int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq)
int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq)
{
	int result;
	unsigned long outlet;
@@ -237,17 +261,27 @@ int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq)
		return result;
	}

	result = ps3_alloc_irq(cpu, outlet, virq);
	result = ps3_irq_plug_setup(cpu, outlet, virq);
	BUG_ON(result);

	return result;
}
EXPORT_SYMBOL_GPL(ps3_event_receive_port_setup);

/**
 * ps3_event_receive_port_destroy - Destroy an event receive port.
 * @virq: The assigned Linux virq.
 *
 * Since ps3_event_receive_port_destroy destroys the receive port outlet,
 * SB devices need to call disconnect_interrupt_event_receive_port() before
 * this.
 */

int ps3_free_event_irq(unsigned int virq)
int ps3_event_receive_port_destroy(unsigned int virq)
{
	int result;

	pr_debug(" -> %s:%d\n", __func__, __LINE__);
	pr_debug(" -> %s:%d virq: %u\n", __func__, __LINE__, virq);

	result = lv1_destruct_event_receive_port(virq_to_hw(virq));

@@ -255,11 +289,17 @@ int ps3_free_event_irq(unsigned int virq)
		pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n",
			__func__, __LINE__, ps3_result(result));

	ps3_free_irq(virq);
	/* lv1_destruct_event_receive_port() destroys the IRQ plug,
	 * so don't call ps3_irq_plug_destroy() here.
	 */

	result = ps3_virq_destroy(virq);
	BUG_ON(result);

	pr_debug(" <- %s:%d\n", __func__, __LINE__);
	return result;
}
EXPORT_SYMBOL_GPL(ps3_event_receive_port_destroy);

int ps3_send_event_locally(unsigned int virq)
{
@@ -267,7 +307,7 @@ int ps3_send_event_locally(unsigned int virq)
}

/**
 * ps3_connect_event_irq - Assign a virq to a system bus device.
 * ps3_sb_event_receive_port_setup - Setup a system bus event receive port.
 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
 * serviced on.
 * @did: The HV device identifier read from the system repository.
@@ -278,13 +318,15 @@ int ps3_send_event_locally(unsigned int virq)
 * coresponds to the software interrupt number.
 */

int ps3_connect_event_irq(enum ps3_cpu_binding cpu,
int ps3_sb_event_receive_port_setup(enum ps3_cpu_binding cpu,
	const struct ps3_device_id *did, unsigned int interrupt_id,
	unsigned int *virq)
{
	/* this should go in system-bus.c */

	int result;

	result = ps3_alloc_event_irq(cpu, virq);
	result = ps3_event_receive_port_setup(cpu, virq);

	if (result)
		return result;
@@ -296,7 +338,7 @@ int ps3_connect_event_irq(enum ps3_cpu_binding cpu,
		pr_debug("%s:%d: lv1_connect_interrupt_event_receive_port"
			" failed: %s\n", __func__, __LINE__,
			ps3_result(result));
		ps3_free_event_irq(*virq);
		ps3_event_receive_port_destroy(*virq);
		*virq = NO_IRQ;
		return result;
	}
@@ -306,10 +348,13 @@ int ps3_connect_event_irq(enum ps3_cpu_binding cpu,

	return 0;
}
EXPORT_SYMBOL(ps3_sb_event_receive_port_setup);

int ps3_disconnect_event_irq(const struct ps3_device_id *did,
int ps3_sb_event_receive_port_destroy(const struct ps3_device_id *did,
	unsigned int interrupt_id, unsigned int virq)
{
	/* this should go in system-bus.c */

	int result;

	pr_debug(" -> %s:%d: interrupt_id %u, virq %u\n", __func__, __LINE__,
@@ -323,14 +368,65 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did,
			" failed: %s\n", __func__, __LINE__,
			ps3_result(result));

	ps3_free_event_irq(virq);
	result = ps3_event_receive_port_destroy(virq);
	BUG_ON(result);

	pr_debug(" <- %s:%d\n", __func__, __LINE__);
	return result;
}
EXPORT_SYMBOL(ps3_sb_event_receive_port_destroy);

/**
 * ps3_io_irq_setup - Setup a system bus io irq.
 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
 * serviced on.
 * @interrupt_id: The device interrupt id read from the system repository.
 * @virq: The assigned Linux virq.
 *
 * An io irq represents a non-virtualized device interrupt.  interrupt_id
 * coresponds to the interrupt number of the interrupt controller.
 */

int ps3_io_irq_setup(enum ps3_cpu_binding cpu, unsigned int interrupt_id,
	unsigned int *virq)
{
	int result;
	unsigned long outlet;

	result = lv1_construct_io_irq_outlet(interrupt_id, &outlet);

	if (result) {
		pr_debug("%s:%d: lv1_construct_io_irq_outlet failed: %s\n",
			__func__, __LINE__, ps3_result(result));
		return result;
	}

	result = ps3_irq_plug_setup(cpu, outlet, virq);
	BUG_ON(result);

	return result;
}
EXPORT_SYMBOL_GPL(ps3_io_irq_setup);

int ps3_io_irq_destroy(unsigned int virq)
{
	int result;

	result = lv1_destruct_io_irq_outlet(virq_to_hw(virq));

	if (result)
		pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
			__func__, __LINE__, ps3_result(result));

	result = ps3_irq_plug_destroy(virq);
	BUG_ON(result);

	return result;
}
EXPORT_SYMBOL_GPL(ps3_io_irq_destroy);

/**
 * ps3_alloc_vuart_irq - Configure the system virtual uart virq.
 * ps3_vuart_irq_setup - Setup the system virtual uart virq.
 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
 * serviced on.
 * @virt_addr_bmp: The caller supplied virtual uart interrupt bitmap.
@@ -340,7 +436,7 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did,
 * freeing the interrupt will return a wrong state error.
 */

int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp,
int ps3_vuart_irq_setup(enum ps3_cpu_binding cpu, void* virt_addr_bmp,
	unsigned int *virq)
{
	int result;
@@ -359,13 +455,13 @@ int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp,
		return result;
	}

	result = ps3_alloc_irq(cpu, outlet, virq);
	result = ps3_irq_plug_setup(cpu, outlet, virq);
	BUG_ON(result);

	return result;
}

int ps3_free_vuart_irq(unsigned int virq)
int ps3_vuart_irq_destroy(unsigned int virq)
{
	int result;

@@ -377,13 +473,14 @@ int ps3_free_vuart_irq(unsigned int virq)
		return result;
	}

	ps3_free_irq(virq);
	result = ps3_irq_plug_destroy(virq);
	BUG_ON(result);

	return result;
}

/**
 * ps3_alloc_spe_irq - Configure an spe virq.
 * ps3_spe_irq_setup - Setup an spe virq.
 * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
 * serviced on.
 * @spe_id: The spe_id returned from lv1_construct_logical_spe().
@@ -392,7 +489,7 @@ int ps3_free_vuart_irq(unsigned int virq)
 *
 */

int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id,
int ps3_spe_irq_setup(enum ps3_cpu_binding cpu, unsigned long spe_id,
	unsigned int class, unsigned int *virq)
{
	int result;
@@ -408,15 +505,16 @@ int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id,
		return result;
	}

	result = ps3_alloc_irq(cpu, outlet, virq);
	result = ps3_irq_plug_setup(cpu, outlet, virq);
	BUG_ON(result);

	return result;
}

int ps3_free_spe_irq(unsigned int virq)
int ps3_spe_irq_destroy(unsigned int virq)
{
	ps3_free_irq(virq);
	int result = ps3_irq_plug_destroy(virq);
	BUG_ON(result);
	return 0;
}

+4 −2
Original line number Diff line number Diff line
@@ -110,7 +110,7 @@ static void __init ps3_smp_setup_cpu(int cpu)
	BUILD_BUG_ON(PPC_MSG_DEBUGGER_BREAK != 3);

	for (i = 0; i < MSG_COUNT; i++) {
		result = ps3_alloc_event_irq(cpu, &virqs[i]);
		result = ps3_event_receive_port_setup(cpu, &virqs[i]);

		if (result)
			continue;
@@ -134,11 +134,13 @@ void ps3_smp_cleanup_cpu(int cpu)
	int i;

	DBG(" -> %s:%d: (%d)\n", __func__, __LINE__, cpu);

	for (i = 0; i < MSG_COUNT; i++) {
		ps3_free_event_irq(virqs[i]);
		free_irq(virqs[i], (void*)(long)i);
		ps3_event_receive_port_destroy(virqs[i]);
		virqs[i] = NO_IRQ;
	}

	DBG(" <- %s:%d: (%d)\n", __func__, __LINE__, cpu);
}

+8 −8
Original line number Diff line number Diff line
@@ -230,19 +230,19 @@ static int __init setup_interrupts(struct spu *spu)
{
	int result;

	result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id,
	result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id,
		0, &spu->irqs[0]);

	if (result)
		goto fail_alloc_0;

	result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id,
	result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id,
		1, &spu->irqs[1]);

	if (result)
		goto fail_alloc_1;

	result = ps3_alloc_spe_irq(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id,
	result = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spu_pdata(spu)->spe_id,
		2, &spu->irqs[2]);

	if (result)
@@ -251,9 +251,9 @@ static int __init setup_interrupts(struct spu *spu)
	return result;

fail_alloc_2:
	ps3_free_spe_irq(spu->irqs[1]);
	ps3_spe_irq_destroy(spu->irqs[1]);
fail_alloc_1:
	ps3_free_spe_irq(spu->irqs[0]);
	ps3_spe_irq_destroy(spu->irqs[0]);
fail_alloc_0:
	spu->irqs[0] = spu->irqs[1] = spu->irqs[2] = NO_IRQ;
	return result;
@@ -301,9 +301,9 @@ static int ps3_destroy_spu(struct spu *spu)
	result = lv1_disable_logical_spe(spu_pdata(spu)->spe_id, 0);
	BUG_ON(result);

	ps3_free_spe_irq(spu->irqs[2]);
	ps3_free_spe_irq(spu->irqs[1]);
	ps3_free_spe_irq(spu->irqs[0]);
	ps3_spe_irq_destroy(spu->irqs[2]);
	ps3_spe_irq_destroy(spu->irqs[1]);
	ps3_spe_irq_destroy(spu->irqs[0]);

	spu->irqs[0] = spu->irqs[1] = spu->irqs[2] = NO_IRQ;

+4 −4
Original line number Diff line number Diff line
@@ -886,12 +886,12 @@ static int ps3_vuart_probe(struct device *_dev)

	if (++vuart_bus_priv.use_count == 1) {

		result = ps3_alloc_vuart_irq(PS3_BINDING_CPU_ANY,
		result = ps3_vuart_irq_setup(PS3_BINDING_CPU_ANY,
			(void*)&vuart_bus_priv.bmp.status, &vuart_bus_priv.virq);

		if (result) {
			dev_dbg(&dev->core,
				"%s:%d: ps3_alloc_vuart_irq failed (%d)\n",
				"%s:%d: ps3_vuart_irq_setup failed (%d)\n",
				__func__, __LINE__, result);
			result = -EPERM;
			goto fail_alloc_irq;
@@ -937,7 +937,7 @@ static int ps3_vuart_probe(struct device *_dev)
fail_probe:
	ps3_vuart_set_interrupt_mask(dev, 0);
fail_request_irq:
	ps3_free_vuart_irq(vuart_bus_priv.virq);
	ps3_vuart_irq_destroy(vuart_bus_priv.virq);
	vuart_bus_priv.virq = NO_IRQ;
fail_alloc_irq:
	--vuart_bus_priv.use_count;
@@ -975,7 +975,7 @@ static int ps3_vuart_remove(struct device *_dev)
	if (--vuart_bus_priv.use_count == 0) {
		BUG();
		free_irq(vuart_bus_priv.virq, &vuart_bus_priv);
		ps3_free_vuart_irq(vuart_bus_priv.virq);
		ps3_vuart_irq_destroy(vuart_bus_priv.virq);
		vuart_bus_priv.virq = NO_IRQ;
	}

+2 −2
Original line number Diff line number Diff line
@@ -104,7 +104,7 @@ static int ps3_ehci_sb_probe(struct ps3_system_bus_device *dev)
	dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__,
		__LINE__, dev->m_region->lpar_addr);

	result = ps3_alloc_io_irq(PS3_BINDING_CPU_ANY, dev->interrupt_id, &virq);
	result = ps3_io_irq_setup(PS3_BINDING_CPU_ANY, dev->interrupt_id, &virq);

	if (result) {
		dev_dbg(&dev->core, "%s:%d: ps3_construct_io_irq(%d) failed.\n",
@@ -162,7 +162,7 @@ fail_add_hcd:
fail_ioremap:
	usb_put_hcd(hcd);
fail_create_hcd:
	ps3_free_io_irq(virq);
	ps3_io_irq_destroy(virq);
fail_irq:
	ps3_free_mmio_region(dev->m_region);
fail_mmio:
Loading