]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/powerpc/platforms/ps3/interrupt.c
[POWERPC] ps3: bind interrupt to cpu
[linux-2.6-omap-h63xx.git] / arch / powerpc / platforms / ps3 / interrupt.c
index d3c97fbff7ef6263f35c4985ba370232d6cc0fcc..27afd1f0358a5d682d325754f1cdea66c0ec42ac 100644 (file)
 #define DBG(fmt...) do{if(0)printk(fmt);}while(0)
 #endif
 
+/**
+ * struct ps3_bmp - a per cpu irq status and mask bitmap structure
+ * @status: 256 bit status bitmap indexed by plug
+ * @unused_1:
+ * @mask: 256 bit mask bitmap indexed by plug
+ * @unused_2:
+ * @lock:
+ * @ipi_debug_brk_mask:
+ *
+ * The HV mantains per SMT thread mappings of HV outlet to HV plug on
+ * behalf of the guest.  These mappings are implemented as 256 bit guest
+ * supplied bitmaps indexed by plug number.  The addresses of the bitmaps
+ * are registered with the HV through lv1_configure_irq_state_bitmap().
+ *
+ * The HV supports 256 plugs per thread, assigned as {0..255}, for a total
+ * of 512 plugs supported on a processor.  To simplify the logic this
+ * implementation equates HV plug value to Linux virq value, constrains each
+ * interrupt to have a system wide unique plug number, and limits the range
+ * of the plug values to map into the first dword of the bitmaps.  This
+ * gives a usable range of plug values of  {NUM_ISA_INTERRUPTS..63}.  Note
+ * that there is no constraint on how many in this set an individual thread
+ * can acquire.
+ */
+
+struct ps3_bmp {
+       struct {
+               u64 status;
+               u64 unused_1[3];
+               u64 mask;
+               u64 unused_2[3];
+       };
+       u64 ipi_debug_brk_mask;
+       spinlock_t lock;
+};
+
+/**
+ * struct ps3_private - a per cpu data structure
+ * @bmp: ps3_bmp structure
+ * @node: HV logical_ppe_id
+ * @cpu: HV thread_id
+ */
+
+struct ps3_private {
+       struct ps3_bmp bmp __attribute__ ((aligned (64)));
+       u64 node;
+       unsigned int cpu;
+};
+
+static DEFINE_PER_CPU(struct ps3_private, ps3_private);
+
+static int ps3_connect_irq(enum ps3_cpu_binding cpu, unsigned long outlet,
+       unsigned int *virq)
+{
+       int result;
+       struct ps3_private *pd;
+
+       /* This defines the default interrupt distribution policy. */
+
+       if (cpu == PS3_BINDING_CPU_ANY)
+               cpu = 0;
+
+       pd = &per_cpu(ps3_private, cpu);
+
+       *virq = irq_create_mapping(NULL, outlet);
+
+       if (*virq == NO_IRQ) {
+               pr_debug("%s:%d: irq_create_mapping failed: outlet %lu\n",
+                       __func__, __LINE__, outlet);
+               result = -ENOMEM;
+               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);
+
+       result = set_irq_chip_data(*virq, pd);
+
+       if (result) {
+               pr_debug("%s:%d: set_irq_chip_data failed\n",
+                       __func__, __LINE__);
+               goto fail_set;
+       }
+
+       return result;
+
+fail_set:
+       lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, *virq);
+fail_connect:
+       irq_dispose_mapping(*virq);
+fail_create:
+       return result;
+}
+
+static void ps3_disconnect_irq(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);
+}
+
 /**
  * ps3_alloc_io_irq - Assign a virq to a system bus device.
- * interrupt_id: The device interrupt id read from the system repository.
+ * @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_alloc_io_irq(unsigned int interrupt_id, unsigned int *virq)
+int ps3_alloc_io_irq(enum ps3_cpu_binding cpu, unsigned int interrupt_id,
+       unsigned int *virq)
 {
        int result;
        unsigned long outlet;
@@ -57,12 +182,10 @@ int ps3_alloc_io_irq(unsigned int interrupt_id, unsigned int *virq)
                return result;
        }
 
-       *virq = irq_create_mapping(NULL, outlet);
-
-       pr_debug("%s:%d: interrupt_id %u => outlet %lu, virq %u\n",
-               __func__, __LINE__, interrupt_id, outlet, *virq);
+       result = ps3_connect_irq(cpu, outlet, virq);
+       BUG_ON(result);
 
-       return 0;
+       return result;
 }
 
 int ps3_free_io_irq(unsigned int virq)
@@ -75,13 +198,15 @@ int ps3_free_io_irq(unsigned int virq)
                pr_debug("%s:%d: lv1_destruct_io_irq_outlet failed: %s\n",
                        __func__, __LINE__, ps3_result(result));
 
-       irq_dispose_mapping(virq);
+       ps3_disconnect_irq(virq);
 
        return result;
 }
 
 /**
  * ps3_alloc_event_irq - Allocate a virq for use with a system event.
+ * @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
@@ -89,7 +214,7 @@ int ps3_free_io_irq(unsigned int virq)
  * events.
  */
 
-int ps3_alloc_event_irq(unsigned int *virq)
+int ps3_alloc_event_irq(enum ps3_cpu_binding cpu, unsigned int *virq)
 {
        int result;
        unsigned long outlet;
@@ -103,12 +228,10 @@ int ps3_alloc_event_irq(unsigned int *virq)
                return result;
        }
 
-       *virq = irq_create_mapping(NULL, outlet);
-
-       pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__, outlet,
-               *virq);
+       result = ps3_connect_irq(cpu, outlet, virq);
+       BUG_ON(result);
 
-       return 0;
+       return result;
 }
 
 int ps3_free_event_irq(unsigned int virq)
@@ -123,7 +246,7 @@ 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));
 
-       irq_dispose_mapping(virq);
+       ps3_disconnect_irq(virq);
 
        pr_debug(" <- %s:%d\n", __func__, __LINE__);
        return result;
@@ -136,6 +259,8 @@ int ps3_send_event_locally(unsigned int virq)
 
 /**
  * ps3_connect_event_irq - Assign a virq to a system bus device.
+ * @cpu: enum ps3_cpu_binding indicating the cpu the interrupt should be
+ * serviced on.
  * @did: The HV device identifier read from the system repository.
  * @interrupt_id: The device interrupt id read from the system repository.
  * @virq: The assigned Linux virq.
@@ -144,12 +269,13 @@ int ps3_send_event_locally(unsigned int virq)
  * coresponds to the software interrupt number.
  */
 
-int ps3_connect_event_irq(const struct ps3_device_id *did,
-       unsigned int interrupt_id, unsigned int *virq)
+int ps3_connect_event_irq(enum ps3_cpu_binding cpu,
+       const struct ps3_device_id *did, unsigned int interrupt_id,
+       unsigned int *virq)
 {
        int result;
 
-       result = ps3_alloc_event_irq(virq);
+       result = ps3_alloc_event_irq(cpu, virq);
 
        if (result)
                return result;
@@ -196,6 +322,8 @@ int ps3_disconnect_event_irq(const struct ps3_device_id *did,
 
 /**
  * ps3_alloc_vuart_irq - Configure 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.
  * @virq: The assigned Linux virq.
  *
@@ -203,13 +331,14 @@ 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(void* virt_addr_bmp, unsigned int *virq)
+int ps3_alloc_vuart_irq(enum ps3_cpu_binding cpu, void* virt_addr_bmp,
+       unsigned int *virq)
 {
        int result;
        unsigned long outlet;
-       unsigned long lpar_addr;
+       u64 lpar_addr;
 
-       BUG_ON(!is_kernel_addr((unsigned long)virt_addr_bmp));
+       BUG_ON(!is_kernel_addr((u64)virt_addr_bmp));
 
        lpar_addr = ps3_mm_phys_to_lpar(__pa(virt_addr_bmp));
 
@@ -221,12 +350,10 @@ int ps3_alloc_vuart_irq(void* virt_addr_bmp, unsigned int *virq)
                return result;
        }
 
-       *virq = irq_create_mapping(NULL, outlet);
-
-       pr_debug("%s:%d: outlet %lu, virq %u\n", __func__, __LINE__,
-               outlet, *virq);
+       result = ps3_connect_irq(cpu, outlet, virq);
+       BUG_ON(result);
 
-       return 0;
+       return result;
 }
 
 int ps3_free_vuart_irq(unsigned int virq)
@@ -241,21 +368,23 @@ int ps3_free_vuart_irq(unsigned int virq)
                return result;
        }
 
-       irq_dispose_mapping(virq);
+       ps3_disconnect_irq(virq);
 
        return result;
 }
 
 /**
  * ps3_alloc_spe_irq - Configure 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().
  * @class: The spe interrupt class {0,1,2}.
  * @virq: The assigned Linux virq.
  *
  */
 
-int ps3_alloc_spe_irq(unsigned long spe_id, unsigned int class,
-       unsigned int *virq)
+int ps3_alloc_spe_irq(enum ps3_cpu_binding cpu, unsigned long spe_id,
+       unsigned int class, unsigned int *virq)
 {
        int result;
        unsigned long outlet;
@@ -270,74 +399,23 @@ int ps3_alloc_spe_irq(unsigned long spe_id, unsigned int class,
                return result;
        }
 
-       *virq = irq_create_mapping(NULL, outlet);
-
-       pr_debug("%s:%d: spe_id %lu, class %u, outlet %lu, virq %u\n",
-               __func__, __LINE__, spe_id, class, outlet, *virq);
+       result = ps3_connect_irq(cpu, outlet, virq);
+       BUG_ON(result);
 
-       return 0;
+       return result;
 }
 
 int ps3_free_spe_irq(unsigned int virq)
 {
-       irq_dispose_mapping(virq);
+       ps3_disconnect_irq(virq);
        return 0;
 }
 
 #define PS3_INVALID_OUTLET ((irq_hw_number_t)-1)
 #define PS3_PLUG_MAX 63
 
-/**
- * struct ps3_bmp - a per cpu irq status and mask bitmap structure
- * @status: 256 bit status bitmap indexed by plug
- * @unused_1:
- * @mask: 256 bit mask bitmap indexed by plug
- * @unused_2:
- * @lock:
- * @ipi_debug_brk_mask:
- *
- * The HV mantains per SMT thread mappings of HV outlet to HV plug on
- * behalf of the guest.  These mappings are implemented as 256 bit guest
- * supplied bitmaps indexed by plug number.  The address of the bitmaps are
- * registered with the HV through lv1_configure_irq_state_bitmap().
- *
- * The HV supports 256 plugs per thread, assigned as {0..255}, for a total
- * of 512 plugs supported on a processor.  To simplify the logic this
- * implementation equates HV plug value to linux virq value, constrains each
- * interrupt to have a system wide unique plug number, and limits the range
- * of the plug values to map into the first dword of the bitmaps.  This
- * gives a usable range of plug values of  {NUM_ISA_INTERRUPTS..63}.  Note
- * that there is no constraint on how many in this set an individual thread
- * can aquire.
- */
-
-struct ps3_bmp {
-       struct {
-               unsigned long status;
-               unsigned long unused_1[3];
-               unsigned long mask;
-               unsigned long unused_2[3];
-       } __attribute__ ((aligned (64)));
-
-       spinlock_t lock;
-       unsigned long ipi_debug_brk_mask;
-};
-
-/**
- * struct ps3_private - a per cpu data structure
- * @bmp: ps3_bmp structure
- * @node: HV logical_ppe_id
- * @cpu: HV thread_id
- */
-
-struct ps3_private {
-       struct ps3_bmp bmp;
-       unsigned long node;
-       unsigned int cpu;
-};
-
 #if defined(DEBUG)
-static void _dump_64_bmp(const char *header, const unsigned long *p, unsigned cpu,
+static void _dump_64_bmp(const char *header, const u64 *p, unsigned cpu,
        const char* func, int line)
 {
        pr_debug("%s:%d: %s %u {%04lx_%04lx_%04lx_%04lx}\n",
@@ -347,7 +425,7 @@ static void _dump_64_bmp(const char *header, const unsigned long *p, unsigned cp
 }
 
 static void __attribute__ ((unused)) _dump_256_bmp(const char *header,
-       const unsigned long *p, unsigned cpu, const char* func, int line)
+       const u64 *p, unsigned cpu, const char* func, int line)
 {
        pr_debug("%s:%d: %s %u {%016lx:%016lx:%016lx:%016lx}\n",
                func, line, header, cpu, p[0], p[1], p[2], p[3]);
@@ -381,9 +459,9 @@ static void dump_bmp(struct ps3_private* pd) {};
 static void ps3_chip_mask(unsigned int virq)
 {
        struct ps3_private *pd = get_irq_chip_data(virq);
-       unsigned long bit = 0x8000000000000000UL >> virq;
-       unsigned long *p = &pd->bmp.mask;
-       unsigned long old;
+       u64 bit = 0x8000000000000000UL >> virq;
+       u64 *p = &pd->bmp.mask;
+       u64 old;
        unsigned long flags;
 
        pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
@@ -405,9 +483,9 @@ static void ps3_chip_mask(unsigned int virq)
 static void ps3_chip_unmask(unsigned int virq)
 {
        struct ps3_private *pd = get_irq_chip_data(virq);
-       unsigned long bit = 0x8000000000000000UL >> virq;
-       unsigned long *p = &pd->bmp.mask;
-       unsigned long old;
+       u64 bit = 0x8000000000000000UL >> virq;
+       u64 *p = &pd->bmp.mask;
+       u64 old;
        unsigned long flags;
 
        pr_debug("%s:%d: cpu %u, virq %d\n", __func__, __LINE__, pd->cpu, virq);
@@ -441,45 +519,18 @@ static struct irq_chip irq_chip = {
 
 static void ps3_host_unmap(struct irq_host *h, 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);
-
-       lv1_disconnect_irq_plug_ext(pd->node, pd->cpu, virq);
-
-       result = set_irq_chip_data(virq, NULL);
-       BUG_ON(result);
+       set_irq_chip_data(virq, NULL);
 }
 
-static DEFINE_PER_CPU(struct ps3_private, ps3_private);
-
 static int ps3_host_map(struct irq_host *h, unsigned int virq,
        irq_hw_number_t hwirq)
 {
-       int result;
-       struct ps3_private *pd = &__get_cpu_var(ps3_private);
-
-       pr_debug("%s:%d: node %lu, cpu %d, hwirq %lu => virq %u\n", __func__,
-               __LINE__, pd->node, pd->cpu, hwirq, virq);
-
-       /* Binds this virq to pd->cpu (current cpu) */
-
-       result = lv1_connect_irq_plug_ext(pd->node, pd->cpu, virq, hwirq, 0);
-
-       if (result) {
-               pr_info("%s:%d: lv1_connect_irq_plug_ext failed:"
-                       " %s\n", __func__, __LINE__, ps3_result(result));
-               return -EPERM;
-       }
-
-       result = set_irq_chip_data(virq, pd);
-       BUG_ON(result);
+       pr_debug("%s:%d: hwirq %lu, virq %u\n", __func__, __LINE__, hwirq,
+               virq);
 
        set_irq_chip_and_handler(virq, &irq_chip, handle_fasteoi_irq);
 
-       return result;
+       return 0;
 }
 
 static struct irq_host_ops ps3_host_ops = {
@@ -500,7 +551,7 @@ void __init ps3_register_ipi_debug_brk(unsigned int cpu, unsigned int virq)
 unsigned int ps3_get_irq(void)
 {
        struct ps3_private *pd = &__get_cpu_var(ps3_private);
-       unsigned long x = (pd->bmp.status & pd->bmp.mask);
+       u64 x = (pd->bmp.status & pd->bmp.mask);
        unsigned int plug;
 
        /* check for ipi break first to stop this cpu ASAP */