📄 interrupt.c
字号:
/* * PS3 interrupt routines. * * Copyright (C) 2006 Sony Computer Entertainment Inc. * Copyright 2006 Sony Corp. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <linux/kernel.h>#include <linux/module.h>#include <linux/irq.h>#include <asm/machdep.h>#include <asm/udbg.h>#include <asm/lv1call.h>#include <asm/smp.h>#include "platform.h"#if defined(DEBUG)#define DBG udbg_printf#else#define DBG pr_debug#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 requires that the 512 bits of status + mask not cross a page * boundary. PS3_BMP_MINALIGN is used to define this minimal 64 byte * alignment. * * 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. */#define PS3_BMP_MINALIGN 64struct 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 * @ppe_id: HV logical_ppe_id * @thread_id: HV thread_id */struct ps3_private { struct ps3_bmp bmp __attribute__ ((aligned (PS3_BMP_MINALIGN))); u64 ppe_id; u64 thread_id;};static DEFINE_PER_CPU(struct ps3_private, ps3_private);/** * ps3_chip_mask - Set an interrupt mask bit in ps3_bmp. * @virq: The assigned Linux virq. * * Sets ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). */static void ps3_chip_mask(unsigned int virq){ struct ps3_private *pd = get_irq_chip_data(virq); unsigned long flags; pr_debug("%s:%d: thread_id %lu, virq %d\n", __func__, __LINE__, pd->thread_id, virq); local_irq_save(flags); clear_bit(63 - virq, &pd->bmp.mask); lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id); local_irq_restore(flags);}/** * ps3_chip_unmask - Clear an interrupt mask bit in ps3_bmp. * @virq: The assigned Linux virq. * * Clears ps3_bmp.mask and calls lv1_did_update_interrupt_mask(). */static void ps3_chip_unmask(unsigned int virq){ struct ps3_private *pd = get_irq_chip_data(virq); unsigned long flags; pr_debug("%s:%d: thread_id %lu, virq %d\n", __func__, __LINE__, pd->thread_id, virq); local_irq_save(flags); set_bit(63 - virq, &pd->bmp.mask); lv1_did_update_interrupt_mask(pd->ppe_id, pd->thread_id); local_irq_restore(flags);}/** * ps3_chip_eoi - HV end-of-interrupt. * @virq: The assigned Linux virq. * * Calls lv1_end_of_interrupt_ext(). */static void ps3_chip_eoi(unsigned int virq){ const struct ps3_private *pd = get_irq_chip_data(virq); lv1_end_of_interrupt_ext(pd->ppe_id, pd->thread_id, virq);}/** * ps3_irq_chip - Represents the ps3_bmp as a Linux struct irq_chip. */static struct irq_chip ps3_irq_chip = { .typename = "ps3", .mask = ps3_chip_mask, .unmask = ps3_chip_unmask, .eoi = ps3_chip_eoi,};/** * 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; 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; } 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; } ps3_chip_mask(*virq); return result;fail_set: irq_dispose_mapping(*virq);fail_create: return result;}/** * 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){ const struct ps3_private *pd = get_irq_chip_data(virq); pr_debug("%s:%d: ppe_id %lu, thread_id %lu, virq %u\n", __func__, __LINE__, pd->ppe_id, pd->thread_id, virq); set_irq_chip_data(virq, NULL); irq_dispose_mapping(virq); pr_debug("%s:%d <-\n", __func__, __LINE__); return 0;}/** * ps3_irq_plug_setup - Generic outlet and 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. * * Sets up virq and connects the irq plug. */int ps3_irq_plug_setup(enum ps3_cpu_binding cpu, unsigned long outlet, unsigned int *virq){ int result; struct ps3_private *pd; result = ps3_virq_setup(cpu, outlet, virq); if (result) { 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->ppe_id, pd->thread_id, *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; } return result;fail_connect: ps3_virq_destroy(*virq);fail_setup: return result;}EXPORT_SYMBOL_GPL(ps3_irq_plug_setup);/** * 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); pr_debug("%s:%d: ppe_id %lu, thread_id %lu, virq %u\n", __func__, __LINE__, pd->ppe_id, pd->thread_id, virq); ps3_chip_mask(virq); result = lv1_disconnect_irq_plug_ext(pd->ppe_id, pd->thread_id, virq); if (result) pr_info("%s:%d: lv1_disconnect_irq_plug_ext failed: %s\n", __func__, __LINE__, ps3_result(result)); ps3_virq_destroy(virq); return result;}EXPORT_SYMBOL_GPL(ps3_irq_plug_destroy);/** * 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 interrupts from system-bus devices, or with * ps3_send_event_locally() to signal events. */int ps3_event_receive_port_setup(enum ps3_cpu_binding cpu, unsigned int *virq){ int result; unsigned long outlet; result = lv1_construct_event_receive_port(&outlet); if (result) { pr_debug("%s:%d: lv1_construct_event_receive_port failed: %s\n", __func__, __LINE__, ps3_result(result)); *virq = NO_IRQ; return result; } 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_event_receive_port_destroy(unsigned int virq){ int result; pr_debug(" -> %s:%d virq %u\n", __func__, __LINE__, virq); ps3_chip_mask(virq); result = lv1_destruct_event_receive_port(virq_to_hw(virq)); if (result) pr_debug("%s:%d: lv1_destruct_event_receive_port failed: %s\n", __func__, __LINE__, ps3_result(result)); /* * Don't call ps3_virq_destroy() here since ps3_smp_cleanup_cpu() * calls from interrupt context (smp_call_function) when kexecing. */ pr_debug(" <- %s:%d\n", __func__, __LINE__); return result;}int ps3_send_event_locally(unsigned int virq){ return lv1_send_event_locally(virq_to_hw(virq));}/** * 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. * @dev: The system bus device instance. * @virq: The assigned Linux virq. * * An event irq represents a virtual device interrupt. The interrupt_id * coresponds to the software interrupt number.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -