From 57b165948fe0e1cca78bca1fe1e1ae9a38baedd2 Mon Sep 17 00:00:00 2001 From: Michael D Labriola Date: Wed, 1 Feb 2012 10:05:00 -0500 Subject: x86/reboot: Reduce to a single DMI table for reboot quirks This commit reduces the X86_32 reboot_dmi_table and the X86_64 pci_reboot_dmi_table into a single table with a single set of functions (e.g., only 1 call to core_initcall). The table entries that use set_bios_reboot are grouped together inside a #define CONFIG_X86_32 block. Note that there's a single entry that uses set_kbd_reboot, which used to be available only on X86_32. This commit moves that entry outside the X86_32 block because it seems it never should have been in there. There's multiple places in reboot.c that assume KBD is valid regardless of X86_32/X86_64. Signed-off-by: Michael D Labriola Cc: Matthew Garrett Link: http://lkml.kernel.org/n/tip-lv3aliubas2l3aenq8v3uklk@git.kernel.org Signed-off-by: Ingo Molnar --- arch/x86/kernel/reboot.c | 176 ++++++++++++++++++++++------------------------- 1 file changed, 83 insertions(+), 93 deletions(-) (limited to 'arch') diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index d840e69a853c..e73973769076 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -150,6 +150,80 @@ static int __init set_bios_reboot(const struct dmi_system_id *d) return 0; } +extern const unsigned char machine_real_restart_asm[]; +extern const u64 machine_real_restart_gdt[3]; + +void machine_real_restart(unsigned int type) +{ + void *restart_va; + unsigned long restart_pa; + void (*restart_lowmem)(unsigned int); + u64 *lowmem_gdt; + + local_irq_disable(); + + /* Write zero to CMOS register number 0x0f, which the BIOS POST + routine will recognize as telling it to do a proper reboot. (Well + that's what this book in front of me says -- it may only apply to + the Phoenix BIOS though, it's not clear). At the same time, + disable NMIs by setting the top bit in the CMOS address register, + as we're about to do peculiar things to the CPU. I'm not sure if + `outb_p' is needed instead of just `outb'. Use it to be on the + safe side. (Yes, CMOS_WRITE does outb_p's. - Paul G.) + */ + spin_lock(&rtc_lock); + CMOS_WRITE(0x00, 0x8f); + spin_unlock(&rtc_lock); + + /* + * Switch back to the initial page table. + */ + load_cr3(initial_page_table); + + /* Write 0x1234 to absolute memory location 0x472. The BIOS reads + this on booting to tell it to "Bypass memory test (also warm + boot)". This seems like a fairly standard thing that gets set by + REBOOT.COM programs, and the previous reset routine did this + too. */ + *((unsigned short *)0x472) = reboot_mode; + + /* Patch the GDT in the low memory trampoline */ + lowmem_gdt = TRAMPOLINE_SYM(machine_real_restart_gdt); + + restart_va = TRAMPOLINE_SYM(machine_real_restart_asm); + restart_pa = virt_to_phys(restart_va); + restart_lowmem = (void (*)(unsigned int))restart_pa; + + /* GDT[0]: GDT self-pointer */ + lowmem_gdt[0] = + (u64)(sizeof(machine_real_restart_gdt) - 1) + + ((u64)virt_to_phys(lowmem_gdt) << 16); + /* GDT[1]: 64K real mode code segment */ + lowmem_gdt[1] = + GDT_ENTRY(0x009b, restart_pa, 0xffff); + + /* Jump to the identity-mapped low memory code */ + restart_lowmem(type); +} +#ifdef CONFIG_APM_MODULE +EXPORT_SYMBOL(machine_real_restart); +#endif + +#endif /* CONFIG_X86_32 */ + +/* + * Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot + */ +static int __init set_pci_reboot(const struct dmi_system_id *d) +{ + if (reboot_type != BOOT_CF9) { + reboot_type = BOOT_CF9; + printk(KERN_INFO "%s series board detected. " + "Selecting PCI-method for reboots.\n", d->ident); + } + return 0; +} + static int __init set_kbd_reboot(const struct dmi_system_id *d) { if (reboot_type != BOOT_KBD) { @@ -159,7 +233,11 @@ static int __init set_kbd_reboot(const struct dmi_system_id *d) return 0; } +/* This is a single dmi_table handling all reboot quirks. Note that + * REBOOT_BIOS is only available for 32bit + */ static struct dmi_system_id __initdata reboot_dmi_table[] = { +#ifdef CONFIG_X86_32 { /* Handle problems with rebooting on Dell E520's */ .callback = set_bios_reboot, .ident = "Dell E520", @@ -309,6 +387,8 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { DMI_MATCH(DMI_BOARD_NAME, "P4S800"), }, }, +#endif /* CONFIG_X86_32 */ + { /* Handle reboot issue on Acer Aspire one */ .callback = set_kbd_reboot, .ident = "Acer Aspire One A110", @@ -317,96 +397,6 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "AOA110"), }, }, - { } -}; - -static int __init reboot_init(void) -{ - /* Only do the DMI check if reboot_type hasn't been overridden - * on the command line - */ - if (reboot_default) { - dmi_check_system(reboot_dmi_table); - } - return 0; -} -core_initcall(reboot_init); - -extern const unsigned char machine_real_restart_asm[]; -extern const u64 machine_real_restart_gdt[3]; - -void machine_real_restart(unsigned int type) -{ - void *restart_va; - unsigned long restart_pa; - void (*restart_lowmem)(unsigned int); - u64 *lowmem_gdt; - - local_irq_disable(); - - /* Write zero to CMOS register number 0x0f, which the BIOS POST - routine will recognize as telling it to do a proper reboot. (Well - that's what this book in front of me says -- it may only apply to - the Phoenix BIOS though, it's not clear). At the same time, - disable NMIs by setting the top bit in the CMOS address register, - as we're about to do peculiar things to the CPU. I'm not sure if - `outb_p' is needed instead of just `outb'. Use it to be on the - safe side. (Yes, CMOS_WRITE does outb_p's. - Paul G.) - */ - spin_lock(&rtc_lock); - CMOS_WRITE(0x00, 0x8f); - spin_unlock(&rtc_lock); - - /* - * Switch back to the initial page table. - */ - load_cr3(initial_page_table); - - /* Write 0x1234 to absolute memory location 0x472. The BIOS reads - this on booting to tell it to "Bypass memory test (also warm - boot)". This seems like a fairly standard thing that gets set by - REBOOT.COM programs, and the previous reset routine did this - too. */ - *((unsigned short *)0x472) = reboot_mode; - - /* Patch the GDT in the low memory trampoline */ - lowmem_gdt = TRAMPOLINE_SYM(machine_real_restart_gdt); - - restart_va = TRAMPOLINE_SYM(machine_real_restart_asm); - restart_pa = virt_to_phys(restart_va); - restart_lowmem = (void (*)(unsigned int))restart_pa; - - /* GDT[0]: GDT self-pointer */ - lowmem_gdt[0] = - (u64)(sizeof(machine_real_restart_gdt) - 1) + - ((u64)virt_to_phys(lowmem_gdt) << 16); - /* GDT[1]: 64K real mode code segment */ - lowmem_gdt[1] = - GDT_ENTRY(0x009b, restart_pa, 0xffff); - - /* Jump to the identity-mapped low memory code */ - restart_lowmem(type); -} -#ifdef CONFIG_APM_MODULE -EXPORT_SYMBOL(machine_real_restart); -#endif - -#endif /* CONFIG_X86_32 */ - -/* - * Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot - */ -static int __init set_pci_reboot(const struct dmi_system_id *d) -{ - if (reboot_type != BOOT_CF9) { - reboot_type = BOOT_CF9; - printk(KERN_INFO "%s series board detected. " - "Selecting PCI-method for reboots.\n", d->ident); - } - return 0; -} - -static struct dmi_system_id __initdata pci_reboot_dmi_table[] = { { /* Handle problems with rebooting on Apple MacBook5 */ .callback = set_pci_reboot, .ident = "Apple MacBook5", @@ -474,17 +464,17 @@ static struct dmi_system_id __initdata pci_reboot_dmi_table[] = { { } }; -static int __init pci_reboot_init(void) +static int __init reboot_init(void) { /* Only do the DMI check if reboot_type hasn't been overridden * on the command line */ if (reboot_default) { - dmi_check_system(pci_reboot_dmi_table); + dmi_check_system(reboot_dmi_table); } return 0; } -core_initcall(pci_reboot_init); +core_initcall(reboot_init); static inline void kb_wait(void) { -- cgit v1.2.3 From 144d102b926f887d3d9f909b69a5c4f504ae0d40 Mon Sep 17 00:00:00 2001 From: Michael D Labriola Date: Wed, 1 Feb 2012 10:06:34 -0500 Subject: x86/reboot: Clean up coding style This commit simply cleans up the style used in this file to fall in line with what's specified in CodingStyle. Mostly comment changes, with a single removal of unneeded braces. Note that the comments for all the DMI quirks in reboot_dmi_table now all line up consistently using tabs instead of spaces. Signed-off-by: Michael D Labriola Link: http://lkml.kernel.org/n/tip-lde9yy7qsomh0sdqevn7xp56@git.kernel.org [ Fixed a few small details. ] Signed-off-by: Ingo Molnar --- arch/x86/kernel/reboot.c | 129 +++++++++++++++++++++++++---------------------- 1 file changed, 70 insertions(+), 59 deletions(-) (limited to 'arch') diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index e73973769076..77215c23fba1 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -39,7 +39,8 @@ static int reboot_mode; enum reboot_type reboot_type = BOOT_ACPI; int reboot_force; -/* This variable is used privately to keep track of whether or not +/* + * This variable is used privately to keep track of whether or not * reboot_type is still set to its default value (i.e., reboot= hasn't * been set on the command line). This is needed so that we can * suppress DMI scanning for reboot quirks. Without it, it's @@ -51,7 +52,8 @@ static int reboot_default = 1; static int reboot_cpu = -1; #endif -/* This is set if we need to go through the 'emergency' path. +/* + * This is set if we need to go through the 'emergency' path. * When machine_emergency_restart() is called, we may be on * an inconsistent state and won't be able to do a clean cleanup */ @@ -60,22 +62,24 @@ static int reboot_emergency; /* This is set by the PCI code if either type 1 or type 2 PCI is detected */ bool port_cf9_safe = false; -/* reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] | p[ci] - warm Don't set the cold reboot flag - cold Set the cold reboot flag - bios Reboot by jumping through the BIOS (only for X86_32) - smp Reboot by executing reset on BSP or other CPU (only for X86_32) - triple Force a triple fault (init) - kbd Use the keyboard controller. cold reset (default) - acpi Use the RESET_REG in the FADT - efi Use efi reset_system runtime service - pci Use the so-called "PCI reset register", CF9 - force Avoid anything that could hang. +/* + * reboot=b[ios] | s[mp] | t[riple] | k[bd] | e[fi] [, [w]arm | [c]old] | p[ci] + * warm Don't set the cold reboot flag + * cold Set the cold reboot flag + * bios Reboot by jumping through the BIOS (only for X86_32) + * smp Reboot by executing reset on BSP or other CPU (only for X86_32) + * triple Force a triple fault (init) + * kbd Use the keyboard controller. cold reset (default) + * acpi Use the RESET_REG in the FADT + * efi Use efi reset_system runtime service + * pci Use the so-called "PCI reset register", CF9 + * force Avoid anything that could hang. */ static int __init reboot_setup(char *str) { for (;;) { - /* Having anything passed on the command line via + /* + * Having anything passed on the command line via * reboot= will cause us to disable DMI checking * below. */ @@ -98,9 +102,11 @@ static int __init reboot_setup(char *str) if (isdigit(*(str+2))) reboot_cpu = reboot_cpu*10 + (int)(*(str+2) - '0'); } - /* we will leave sorting out the final value - when we are ready to reboot, since we might not - have detected BSP APIC ID or smp_num_cpu */ + /* + * We will leave sorting out the final value + * when we are ready to reboot, since we might not + * have detected BSP APIC ID or smp_num_cpu + */ break; #endif /* CONFIG_SMP */ @@ -162,14 +168,15 @@ void machine_real_restart(unsigned int type) local_irq_disable(); - /* Write zero to CMOS register number 0x0f, which the BIOS POST - routine will recognize as telling it to do a proper reboot. (Well - that's what this book in front of me says -- it may only apply to - the Phoenix BIOS though, it's not clear). At the same time, - disable NMIs by setting the top bit in the CMOS address register, - as we're about to do peculiar things to the CPU. I'm not sure if - `outb_p' is needed instead of just `outb'. Use it to be on the - safe side. (Yes, CMOS_WRITE does outb_p's. - Paul G.) + /* + * Write zero to CMOS register number 0x0f, which the BIOS POST + * routine will recognize as telling it to do a proper reboot. (Well + * that's what this book in front of me says -- it may only apply to + * the Phoenix BIOS though, it's not clear). At the same time, + * disable NMIs by setting the top bit in the CMOS address register, + * as we're about to do peculiar things to the CPU. I'm not sure if + * `outb_p' is needed instead of just `outb'. Use it to be on the + * safe side. (Yes, CMOS_WRITE does outb_p's. - Paul G.) */ spin_lock(&rtc_lock); CMOS_WRITE(0x00, 0x8f); @@ -180,11 +187,12 @@ void machine_real_restart(unsigned int type) */ load_cr3(initial_page_table); - /* Write 0x1234 to absolute memory location 0x472. The BIOS reads - this on booting to tell it to "Bypass memory test (also warm - boot)". This seems like a fairly standard thing that gets set by - REBOOT.COM programs, and the previous reset routine did this - too. */ + /* + * Write 0x1234 to absolute memory location 0x472. The BIOS reads + * this on booting to tell it to "Bypass memory test (also warm + * boot)". This seems like a fairly standard thing that gets set by + * REBOOT.COM programs, and the previous reset routine did this + * too. */ *((unsigned short *)0x472) = reboot_mode; /* Patch the GDT in the low memory trampoline */ @@ -233,7 +241,8 @@ static int __init set_kbd_reboot(const struct dmi_system_id *d) return 0; } -/* This is a single dmi_table handling all reboot quirks. Note that +/* + * This is a single dmi_table handling all reboot quirks. Note that * REBOOT_BIOS is only available for 32bit */ static struct dmi_system_id __initdata reboot_dmi_table[] = { @@ -262,7 +271,7 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"), }, }, - { /* Handle problems with rebooting on Dell Optiplex 745's SFF*/ + { /* Handle problems with rebooting on Dell Optiplex 745's SFF */ .callback = set_bios_reboot, .ident = "Dell OptiPlex 745", .matches = { @@ -270,7 +279,7 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"), }, }, - { /* Handle problems with rebooting on Dell Optiplex 745's DFF*/ + { /* Handle problems with rebooting on Dell Optiplex 745's DFF */ .callback = set_bios_reboot, .ident = "Dell OptiPlex 745", .matches = { @@ -279,7 +288,7 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { DMI_MATCH(DMI_BOARD_NAME, "0MM599"), }, }, - { /* Handle problems with rebooting on Dell Optiplex 745 with 0KW626 */ + { /* Handle problems with rebooting on Dell Optiplex 745 with 0KW626 */ .callback = set_bios_reboot, .ident = "Dell OptiPlex 745", .matches = { @@ -288,7 +297,7 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { DMI_MATCH(DMI_BOARD_NAME, "0KW626"), }, }, - { /* Handle problems with rebooting on Dell Optiplex 330 with 0KP561 */ + { /* Handle problems with rebooting on Dell Optiplex 330 with 0KP561 */ .callback = set_bios_reboot, .ident = "Dell OptiPlex 330", .matches = { @@ -297,7 +306,7 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { DMI_MATCH(DMI_BOARD_NAME, "0KP561"), }, }, - { /* Handle problems with rebooting on Dell Optiplex 360 with 0T656F */ + { /* Handle problems with rebooting on Dell Optiplex 360 with 0T656F */ .callback = set_bios_reboot, .ident = "Dell OptiPlex 360", .matches = { @@ -306,7 +315,7 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { DMI_MATCH(DMI_BOARD_NAME, "0T656F"), }, }, - { /* Handle problems with rebooting on Dell OptiPlex 760 with 0G919G*/ + { /* Handle problems with rebooting on Dell OptiPlex 760 with 0G919G */ .callback = set_bios_reboot, .ident = "Dell OptiPlex 760", .matches = { @@ -379,7 +388,7 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_NAME, "SBC-FITPC2"), }, }, - { /* Handle problems with rebooting on ASUS P4S800 */ + { /* Handle problems with rebooting on ASUS P4S800 */ .callback = set_bios_reboot, .ident = "ASUS P4S800", .matches = { @@ -389,7 +398,7 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { }, #endif /* CONFIG_X86_32 */ - { /* Handle reboot issue on Acer Aspire one */ + { /* Handle reboot issue on Acer Aspire one */ .callback = set_kbd_reboot, .ident = "Acer Aspire One A110", .matches = { @@ -466,12 +475,12 @@ static struct dmi_system_id __initdata reboot_dmi_table[] = { static int __init reboot_init(void) { - /* Only do the DMI check if reboot_type hasn't been overridden + /* + * Only do the DMI check if reboot_type hasn't been overridden * on the command line */ - if (reboot_default) { + if (reboot_default) dmi_check_system(reboot_dmi_table); - } return 0; } core_initcall(reboot_init); @@ -492,14 +501,14 @@ static void vmxoff_nmi(int cpu, struct pt_regs *regs) cpu_emergency_vmxoff(); } -/* Use NMIs as IPIs to tell all CPUs to disable virtualization - */ +/* Use NMIs as IPIs to tell all CPUs to disable virtualization */ static void emergency_vmx_disable_all(void) { /* Just make sure we won't change CPUs while doing this */ local_irq_disable(); - /* We need to disable VMX on all CPUs before rebooting, otherwise + /* + * We need to disable VMX on all CPUs before rebooting, otherwise * we risk hanging up the machine, because the CPU ignore INIT * signals when VMX is enabled. * @@ -518,8 +527,7 @@ static void emergency_vmx_disable_all(void) * is still enabling VMX. */ if (cpu_has_vmx() && cpu_vmx_enabled()) { - /* Disable VMX on this CPU. - */ + /* Disable VMX on this CPU. */ cpu_vmxoff(); /* Halt and disable VMX on the other CPUs */ @@ -564,12 +572,12 @@ static void native_machine_emergency_restart(void) /* Could also try the reset bit in the Hammer NB */ switch (reboot_type) { case BOOT_KBD: - mach_reboot_fixups(); /* for board specific fixups */ + mach_reboot_fixups(); /* For board specific fixups */ for (i = 0; i < 10; i++) { kb_wait(); udelay(50); - outb(0xfe, 0x64); /* pulse reset low */ + outb(0xfe, 0x64); /* Pulse reset low */ udelay(50); } if (attempt == 0 && orig_reboot_type == BOOT_ACPI) { @@ -611,7 +619,7 @@ static void native_machine_emergency_restart(void) case BOOT_CF9: port_cf9_safe = true; - /* fall through */ + /* Fall through */ case BOOT_CF9_COND: if (port_cf9_safe) { @@ -649,7 +657,8 @@ void native_machine_shutdown(void) /* Make certain I only run on the appropriate processor */ set_cpus_allowed_ptr(current, cpumask_of(reboot_cpu_id)); - /* O.K Now that I'm on the appropriate processor, + /* + * O.K Now that I'm on the appropriate processor, * stop all of the others. */ stop_other_cpus(); @@ -687,12 +696,11 @@ static void native_machine_restart(char *__unused) static void native_machine_halt(void) { - /* stop other cpus and apics */ + /* Stop other cpus and apics */ machine_shutdown(); tboot_shutdown(TB_SHUTDOWN_HALT); - /* stop this cpu */ stop_this_cpu(NULL); } @@ -703,7 +711,7 @@ static void native_machine_power_off(void) machine_shutdown(); pm_power_off(); } - /* a fallback in case there is no PM info available */ + /* A fallback in case there is no PM info available */ tboot_shutdown(TB_SHUTDOWN_HALT); } @@ -765,7 +773,8 @@ static int crash_nmi_callback(unsigned int val, struct pt_regs *regs) cpu = raw_smp_processor_id(); - /* Don't do anything if this handler is invoked on crashing cpu. + /* + * Don't do anything if this handler is invoked on crashing cpu. * Otherwise, system will completely hang. Crashing cpu can get * an NMI if system was initially booted with nmi_watchdog parameter. */ @@ -789,7 +798,8 @@ static void smp_send_nmi_allbutself(void) apic->send_IPI_allbutself(NMI_VECTOR); } -/* Halt all other CPUs, calling the specified function on each of them +/* + * Halt all other CPUs, calling the specified function on each of them * * This function can be used to halt all other CPUs on crash * or emergency reboot time. The function passed as parameter @@ -800,7 +810,7 @@ void nmi_shootdown_cpus(nmi_shootdown_cb callback) unsigned long msecs; local_irq_disable(); - /* Make a note of crashing cpu. Will be used in NMI callback.*/ + /* Make a note of crashing cpu. Will be used in NMI callback. */ crashing_cpu = safe_smp_processor_id(); shootdown_callback = callback; @@ -809,8 +819,9 @@ void nmi_shootdown_cpus(nmi_shootdown_cb callback) /* Would it be better to replace the trap vector here? */ if (register_nmi_handler(NMI_LOCAL, crash_nmi_callback, NMI_FLAG_FIRST, "crash")) - return; /* return what? */ - /* Ensure the new callback function is set before sending + return; /* Return what? */ + /* + * Ensure the new callback function is set before sending * out the NMI */ wmb(); -- cgit v1.2.3 From b4096504c2b6945c41eae99f85f4ec64ac19bfce Mon Sep 17 00:00:00 2001 From: Danny Kukawka Date: Wed, 15 Feb 2012 09:20:04 +0000 Subject: arch/sparc/kernel/unaligned_64.c: included linux/bitops.h twice arch/sparc/kernel/unaligned_64.c included 'linux/bitops.h' twice, remove the duplicate. Signed-off-by: Danny Kukawka Signed-off-by: David S. Miller --- arch/sparc/kernel/unaligned_64.c | 1 - 1 file changed, 1 deletion(-) (limited to 'arch') diff --git a/arch/sparc/kernel/unaligned_64.c b/arch/sparc/kernel/unaligned_64.c index 76e4ac1a13e1..626e7b06d6b1 100644 --- a/arch/sparc/kernel/unaligned_64.c +++ b/arch/sparc/kernel/unaligned_64.c @@ -22,7 +22,6 @@ #include #include #include -#include #include enum direction { -- cgit v1.2.3 From 2b144498350860b6ee9dc57ff27a93ad488de5dc Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Thu, 9 Feb 2012 14:56:42 +0530 Subject: uprobes, mm, x86: Add the ability to install and remove uprobes breakpoints Add uprobes support to the core kernel, with x86 support. This commit adds the kernel facilities, the actual uprobes user-space ABI and perf probe support comes in later commits. General design: Uprobes are maintained in an rb-tree indexed by inode and offset (the offset here is from the start of the mapping). For a unique (inode, offset) tuple, there can be at most one uprobe in the rb-tree. Since the (inode, offset) tuple identifies a unique uprobe, more than one user may be interested in the same uprobe. This provides the ability to connect multiple 'consumers' to the same uprobe. Each consumer defines a handler and a filter (optional). The 'handler' is run every time the uprobe is hit, if it matches the 'filter' criteria. The first consumer of a uprobe causes the breakpoint to be inserted at the specified address and subsequent consumers are appended to this list. On subsequent probes, the consumer gets appended to the existing list of consumers. The breakpoint is removed when the last consumer unregisters. For all other unregisterations, the consumer is removed from the list of consumers. Given a inode, we get a list of the mms that have mapped the inode. Do the actual registration if mm maps the page where a probe needs to be inserted/removed. We use a temporary list to walk through the vmas that map the inode. - The number of maps that map the inode, is not known before we walk the rmap and keeps changing. - extending vm_area_struct wasn't recommended, it's a size-critical data structure. - There can be more than one maps of the inode in the same mm. We add callbacks to the mmap methods to keep an eye on text vmas that are of interest to uprobes. When a vma of interest is mapped, we insert the breakpoint at the right address. Uprobe works by replacing the instruction at the address defined by (inode, offset) with the arch specific breakpoint instruction. We save a copy of the original instruction at the uprobed address. This is needed for: a. executing the instruction out-of-line (xol). b. instruction analysis for any subsequent fixups. c. restoring the instruction back when the uprobe is unregistered. We insert or delete a breakpoint instruction, and this breakpoint instruction is assumed to be the smallest instruction available on the platform. For fixed size instruction platforms this is trivially true, for variable size instruction platforms the breakpoint instruction is typically the smallest (often a single byte). Writing the instruction is done by COWing the page and changing the instruction during the copy, this even though most platforms allow atomic writes of the breakpoint instruction. This also mirrors the behaviour of a ptrace() memory write to a PRIVATE file map. The core worker is derived from KSM's replace_page() logic. In essence, similar to KSM: a. allocate a new page and copy over contents of the page that has the uprobed vaddr b. modify the copy and insert the breakpoint at the required address c. switch the original page with the copy containing the breakpoint d. flush page tables. replace_page() is being replicated here because of some minor changes in the type of pages and also because Hugh Dickins had plans to improve replace_page() for KSM specific work. Instruction analysis on x86 is based on instruction decoder and determines if an instruction can be probed and determines the necessary fixups after singlestep. Instruction analysis is done at probe insertion time so that we avoid having to repeat the same analysis every time a probe is hit. A lot of code here is due to the improvement/suggestions/inputs from Peter Zijlstra. Changelog: (v10): - Add code to clear REX.B prefix as suggested by Denys Vlasenko and Masami Hiramatsu. (v9): - Use insn_offset_modrm as suggested by Masami Hiramatsu. (v7): Handle comments from Peter Zijlstra: - Dont take reference to inode. (expect inode to uprobe_register to be sane). - Use PTR_ERR to set the return value. - No need to take reference to inode. - use PTR_ERR to return error value. - register and uprobe_unregister share code. (v5): - Modified del_consumer as per comments from Peter. - Drop reference to inode before dropping reference to uprobe. - Use i_size_read(inode) instead of inode->i_size. - Ensure uprobe->consumers is NULL, before __uprobe_unregister() is called. - Includes errno.h as recommended by Stephen Rothwell to fix a build issue on sparc defconfig - Remove restrictions while unregistering. - Earlier code leaked inode references under some conditions while registering/unregistering. - Continue the vma-rmap walk even if the intermediate vma doesnt meet the requirements. - Validate the vma found by find_vma before inserting/removing the breakpoint - Call del_consumer under mutex_lock. - Use hash locks. - Handle mremap. - Introduce find_least_offset_node() instead of close match logic in find_uprobe - Uprobes no more depends on MM_OWNER; No reference to task_structs while inserting/removing a probe. - Uses read_mapping_page instead of grab_cache_page so that the pages have valid content. - pass NULL to get_user_pages for the task parameter. - call SetPageUptodate on the new page allocated in write_opcode. - fix leaking a reference to the new page under certain conditions. - Include Instruction Decoder if Uprobes gets defined. - Remove const attributes for instruction prefix arrays. - Uses mm_context to know if the application is 32 bit. Signed-off-by: Srikar Dronamraju Also-written-by: Jim Keniston Reviewed-by: Peter Zijlstra Cc: Oleg Nesterov Cc: Andi Kleen Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Roland McGrath Cc: Masami Hiramatsu Cc: Arnaldo Carvalho de Melo Cc: Anton Arapov Cc: Ananth N Mavinakayanahalli Cc: Stephen Rothwell Cc: Denys Vlasenko Cc: Peter Zijlstra Cc: Linus Torvalds Cc: Andrew Morton Cc: Linux-mm Link: http://lkml.kernel.org/r/20120209092642.GE16600@linux.vnet.ibm.com [ Made various small edits to the commit log ] Signed-off-by: Ingo Molnar --- arch/Kconfig | 11 + arch/x86/Kconfig | 5 +- arch/x86/include/asm/uprobes.h | 42 ++ arch/x86/kernel/Makefile | 1 + arch/x86/kernel/uprobes.c | 412 +++++++++++++++++ include/linux/uprobes.h | 98 +++++ kernel/Makefile | 1 + kernel/uprobes.c | 976 +++++++++++++++++++++++++++++++++++++++++ mm/mmap.c | 23 + 9 files changed, 1568 insertions(+), 1 deletion(-) create mode 100644 arch/x86/include/asm/uprobes.h create mode 100644 arch/x86/kernel/uprobes.c create mode 100644 include/linux/uprobes.h create mode 100644 kernel/uprobes.c (limited to 'arch') diff --git a/arch/Kconfig b/arch/Kconfig index 4f55c736be11..284f5898f526 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -65,6 +65,17 @@ config OPTPROBES depends on KPROBES && HAVE_OPTPROBES depends on !PREEMPT +config UPROBES + bool "User-space probes (EXPERIMENTAL)" + depends on ARCH_SUPPORTS_UPROBES + default n + help + Uprobes enables kernel subsystems to establish probepoints + in user applications and execute handler functions when + the probepoints are hit. + + If in doubt, say "N". + config HAVE_EFFICIENT_UNALIGNED_ACCESS bool help diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 5bed94e189fa..481dbfcf14ed 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -84,7 +84,7 @@ config X86 select GENERIC_IOMAP config INSTRUCTION_DECODER - def_bool (KPROBES || PERF_EVENTS) + def_bool (KPROBES || PERF_EVENTS || UPROBES) config OUTPUT_FORMAT string @@ -240,6 +240,9 @@ config ARCH_CPU_PROBE_RELEASE def_bool y depends on HOTPLUG_CPU +config ARCH_SUPPORTS_UPROBES + def_bool y + source "init/Kconfig" source "kernel/Kconfig.freezer" diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h new file mode 100644 index 000000000000..8208234391ff --- /dev/null +++ b/arch/x86/include/asm/uprobes.h @@ -0,0 +1,42 @@ +#ifndef _ASM_UPROBES_H +#define _ASM_UPROBES_H +/* + * Userspace Probes (UProbes) for x86 + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * Copyright (C) IBM Corporation, 2008-2011 + * Authors: + * Srikar Dronamraju + * Jim Keniston + */ + +typedef u8 uprobe_opcode_t; +#define MAX_UINSN_BYTES 16 +#define UPROBES_XOL_SLOT_BYTES 128 /* to keep it cache aligned */ + +#define UPROBES_BKPT_INSN 0xcc +#define UPROBES_BKPT_INSN_SIZE 1 + +struct uprobe_arch_info { + u16 fixups; +#ifdef CONFIG_X86_64 + unsigned long rip_rela_target_address; +#endif +}; + +struct uprobe; +extern int analyze_insn(struct mm_struct *mm, struct uprobe *uprobe); +#endif /* _ASM_UPROBES_H */ diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 5369059c07a9..8c8c365a3bc3 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o obj-$(CONFIG_SWIOTLB) += pci-swiotlb.o obj-$(CONFIG_OF) += devicetree.o +obj-$(CONFIG_UPROBES) += uprobes.o ### # 64 bit specific files diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c new file mode 100644 index 000000000000..2a301bb91bdb --- /dev/null +++ b/arch/x86/kernel/uprobes.c @@ -0,0 +1,412 @@ +/* + * Userspace Probes (UProbes) for x86 + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * Copyright (C) IBM Corporation, 2008-2011 + * Authors: + * Srikar Dronamraju + * Jim Keniston + */ + +#include +#include +#include +#include + +#include +#include + +/* Post-execution fixups. */ + +/* No fixup needed */ +#define UPROBES_FIX_NONE 0x0 +/* Adjust IP back to vicinity of actual insn */ +#define UPROBES_FIX_IP 0x1 +/* Adjust the return address of a call insn */ +#define UPROBES_FIX_CALL 0x2 + +#define UPROBES_FIX_RIP_AX 0x8000 +#define UPROBES_FIX_RIP_CX 0x4000 + +/* Adaptations for mhiramat x86 decoder v14. */ +#define OPCODE1(insn) ((insn)->opcode.bytes[0]) +#define OPCODE2(insn) ((insn)->opcode.bytes[1]) +#define OPCODE3(insn) ((insn)->opcode.bytes[2]) +#define MODRM_REG(insn) X86_MODRM_REG(insn->modrm.value) + +#define W(row, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf)\ + (((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) | \ + (b4##UL << 0x4)|(b5##UL << 0x5)|(b6##UL << 0x6)|(b7##UL << 0x7) | \ + (b8##UL << 0x8)|(b9##UL << 0x9)|(ba##UL << 0xa)|(bb##UL << 0xb) | \ + (bc##UL << 0xc)|(bd##UL << 0xd)|(be##UL << 0xe)|(bf##UL << 0xf)) \ + << (row % 32)) + +#ifdef CONFIG_X86_64 +static volatile u32 good_insns_64[256 / 32] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ---------------------------------------------- */ + W(0x00, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) | /* 00 */ + W(0x10, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) , /* 10 */ + W(0x20, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) | /* 20 */ + W(0x30, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) , /* 30 */ + W(0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 40 */ + W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */ + W(0x60, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* 60 */ + W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 70 */ + W(0x80, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */ + W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */ + W(0xa0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* a0 */ + W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* b0 */ + W(0xc0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0) | /* c0 */ + W(0xd0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */ + W(0xe0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* e0 */ + W(0xf0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1) /* f0 */ + /* ---------------------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; +#endif + +/* Good-instruction tables for 32-bit apps */ + +static volatile u32 good_insns_32[256 / 32] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ---------------------------------------------- */ + W(0x00, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0) | /* 00 */ + W(0x10, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0) , /* 10 */ + W(0x20, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1) | /* 20 */ + W(0x30, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1) , /* 30 */ + W(0x40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 40 */ + W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */ + W(0x60, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* 60 */ + W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 70 */ + W(0x80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */ + W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */ + W(0xa0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* a0 */ + W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* b0 */ + W(0xc0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0) | /* c0 */ + W(0xd0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */ + W(0xe0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* e0 */ + W(0xf0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1) /* f0 */ + /* ---------------------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +/* Using this for both 64-bit and 32-bit apps */ +static volatile u32 good_2byte_insns[256 / 32] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ---------------------------------------------- */ + W(0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1) | /* 00 */ + W(0x10, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) , /* 10 */ + W(0x20, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1) | /* 20 */ + W(0x30, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) , /* 30 */ + W(0x40, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 40 */ + W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */ + W(0x60, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 60 */ + W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1) , /* 70 */ + W(0x80, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */ + W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */ + W(0xa0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1) | /* a0 */ + W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1) , /* b0 */ + W(0xc0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* c0 */ + W(0xd0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */ + W(0xe0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* e0 */ + W(0xf0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) /* f0 */ + /* ---------------------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; + +#undef W + +/* + * opcodes we'll probably never support: + * 6c-6d, e4-e5, ec-ed - in + * 6e-6f, e6-e7, ee-ef - out + * cc, cd - int3, int + * cf - iret + * d6 - illegal instruction + * f1 - int1/icebp + * f4 - hlt + * fa, fb - cli, sti + * 0f - lar, lsl, syscall, clts, sysret, sysenter, sysexit, invd, wbinvd, ud2 + * + * invalid opcodes in 64-bit mode: + * 06, 0e, 16, 1e, 27, 2f, 37, 3f, 60-62, 82, c4-c5, d4-d5 + * + * 63 - we support this opcode in x86_64 but not in i386. + * + * opcodes we may need to refine support for: + * 0f - 2-byte instructions: For many of these instructions, the validity + * depends on the prefix and/or the reg field. On such instructions, we + * just consider the opcode combination valid if it corresponds to any + * valid instruction. + * 8f - Group 1 - only reg = 0 is OK + * c6-c7 - Group 11 - only reg = 0 is OK + * d9-df - fpu insns with some illegal encodings + * f2, f3 - repnz, repz prefixes. These are also the first byte for + * certain floating-point instructions, such as addsd. + * fe - Group 4 - only reg = 0 or 1 is OK + * ff - Group 5 - only reg = 0-6 is OK + * + * others -- Do we need to support these? + * 0f - (floating-point?) prefetch instructions + * 07, 17, 1f - pop es, pop ss, pop ds + * 26, 2e, 36, 3e - es:, cs:, ss:, ds: segment prefixes -- + * but 64 and 65 (fs: and gs:) seem to be used, so we support them + * 67 - addr16 prefix + * ce - into + * f0 - lock prefix + */ + +/* + * TODO: + * - Where necessary, examine the modrm byte and allow only valid instructions + * in the different Groups and fpu instructions. + */ + +static bool is_prefix_bad(struct insn *insn) +{ + int i; + + for (i = 0; i < insn->prefixes.nbytes; i++) { + switch (insn->prefixes.bytes[i]) { + case 0x26: /*INAT_PFX_ES */ + case 0x2E: /*INAT_PFX_CS */ + case 0x36: /*INAT_PFX_DS */ + case 0x3E: /*INAT_PFX_SS */ + case 0xF0: /*INAT_PFX_LOCK */ + return true; + } + } + return false; +} + +static int validate_insn_32bits(struct uprobe *uprobe, struct insn *insn) +{ + insn_init(insn, uprobe->insn, false); + + /* Skip good instruction prefixes; reject "bad" ones. */ + insn_get_opcode(insn); + if (is_prefix_bad(insn)) + return -ENOTSUPP; + if (test_bit(OPCODE1(insn), (unsigned long *)good_insns_32)) + return 0; + if (insn->opcode.nbytes == 2) { + if (test_bit(OPCODE2(insn), (unsigned long *)good_2byte_insns)) + return 0; + } + return -ENOTSUPP; +} + +/* + * Figure out which fixups post_xol() will need to perform, and annotate + * uprobe->arch_info.fixups accordingly. To start with, + * uprobe->arch_info.fixups is either zero or it reflects rip-related + * fixups. + */ +static void prepare_fixups(struct uprobe *uprobe, struct insn *insn) +{ + bool fix_ip = true, fix_call = false; /* defaults */ + int reg; + + insn_get_opcode(insn); /* should be a nop */ + + switch (OPCODE1(insn)) { + case 0xc3: /* ret/lret */ + case 0xcb: + case 0xc2: + case 0xca: + /* ip is correct */ + fix_ip = false; + break; + case 0xe8: /* call relative - Fix return addr */ + fix_call = true; + break; + case 0x9a: /* call absolute - Fix return addr, not ip */ + fix_call = true; + fix_ip = false; + break; + case 0xff: + insn_get_modrm(insn); + reg = MODRM_REG(insn); + if (reg == 2 || reg == 3) { + /* call or lcall, indirect */ + /* Fix return addr; ip is correct. */ + fix_call = true; + fix_ip = false; + } else if (reg == 4 || reg == 5) { + /* jmp or ljmp, indirect */ + /* ip is correct. */ + fix_ip = false; + } + break; + case 0xea: /* jmp absolute -- ip is correct */ + fix_ip = false; + break; + default: + break; + } + if (fix_ip) + uprobe->arch_info.fixups |= UPROBES_FIX_IP; + if (fix_call) + uprobe->arch_info.fixups |= UPROBES_FIX_CALL; +} + +#ifdef CONFIG_X86_64 +/* + * If uprobe->insn doesn't use rip-relative addressing, return + * immediately. Otherwise, rewrite the instruction so that it accesses + * its memory operand indirectly through a scratch register. Set + * uprobe->arch_info.fixups and uprobe->arch_info.rip_rela_target_address + * accordingly. (The contents of the scratch register will be saved + * before we single-step the modified instruction, and restored + * afterward.) + * + * We do this because a rip-relative instruction can access only a + * relatively small area (+/- 2 GB from the instruction), and the XOL + * area typically lies beyond that area. At least for instructions + * that store to memory, we can't execute the original instruction + * and "fix things up" later, because the misdirected store could be + * disastrous. + * + * Some useful facts about rip-relative instructions: + * - There's always a modrm byte. + * - There's never a SIB byte. + * - The displacement is always 4 bytes. + */ +static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, + struct insn *insn) +{ + u8 *cursor; + u8 reg; + + if (mm->context.ia32_compat) + return; + + uprobe->arch_info.rip_rela_target_address = 0x0; + if (!insn_rip_relative(insn)) + return; + + /* + * insn_rip_relative() would have decoded rex_prefix, modrm. + * Clear REX.b bit (extension of MODRM.rm field): + * we want to encode rax/rcx, not r8/r9. + */ + if (insn->rex_prefix.nbytes) { + cursor = uprobe->insn + insn_offset_rex_prefix(insn); + *cursor &= 0xfe; /* Clearing REX.B bit */ + } + + /* + * Point cursor at the modrm byte. The next 4 bytes are the + * displacement. Beyond the displacement, for some instructions, + * is the immediate operand. + */ + cursor = uprobe->insn + insn_offset_modrm(insn); + insn_get_length(insn); + + /* + * Convert from rip-relative addressing to indirect addressing + * via a scratch register. Change the r/m field from 0x5 (%rip) + * to 0x0 (%rax) or 0x1 (%rcx), and squeeze out the offset field. + */ + reg = MODRM_REG(insn); + if (reg == 0) { + /* + * The register operand (if any) is either the A register + * (%rax, %eax, etc.) or (if the 0x4 bit is set in the + * REX prefix) %r8. In any case, we know the C register + * is NOT the register operand, so we use %rcx (register + * #1) for the scratch register. + */ + uprobe->arch_info.fixups = UPROBES_FIX_RIP_CX; + /* Change modrm from 00 000 101 to 00 000 001. */ + *cursor = 0x1; + } else { + /* Use %rax (register #0) for the scratch register. */ + uprobe->arch_info.fixups = UPROBES_FIX_RIP_AX; + /* Change modrm from 00 xxx 101 to 00 xxx 000 */ + *cursor = (reg << 3); + } + + /* Target address = address of next instruction + (signed) offset */ + uprobe->arch_info.rip_rela_target_address = (long)insn->length + + insn->displacement.value; + /* Displacement field is gone; slide immediate field (if any) over. */ + if (insn->immediate.nbytes) { + cursor++; + memmove(cursor, cursor + insn->displacement.nbytes, + insn->immediate.nbytes); + } + return; +} + +static int validate_insn_64bits(struct uprobe *uprobe, struct insn *insn) +{ + insn_init(insn, uprobe->insn, true); + + /* Skip good instruction prefixes; reject "bad" ones. */ + insn_get_opcode(insn); + if (is_prefix_bad(insn)) + return -ENOTSUPP; + if (test_bit(OPCODE1(insn), (unsigned long *)good_insns_64)) + return 0; + if (insn->opcode.nbytes == 2) { + if (test_bit(OPCODE2(insn), (unsigned long *)good_2byte_insns)) + return 0; + } + return -ENOTSUPP; +} + +static int validate_insn_bits(struct mm_struct *mm, struct uprobe *uprobe, + struct insn *insn) +{ + if (mm->context.ia32_compat) + return validate_insn_32bits(uprobe, insn); + return validate_insn_64bits(uprobe, insn); +} +#else +static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, + struct insn *insn) +{ + return; +} + +static int validate_insn_bits(struct mm_struct *mm, struct uprobe *uprobe, + struct insn *insn) +{ + return validate_insn_32bits(uprobe, insn); +} +#endif /* CONFIG_X86_64 */ + +/** + * analyze_insn - instruction analysis including validity and fixups. + * @mm: the probed address space. + * @uprobe: the probepoint information. + * Return 0 on success or a -ve number on error. + */ +int analyze_insn(struct mm_struct *mm, struct uprobe *uprobe) +{ + int ret; + struct insn insn; + + uprobe->arch_info.fixups = 0; + ret = validate_insn_bits(mm, uprobe, &insn); + if (ret != 0) + return ret; + handle_riprel_insn(mm, uprobe, &insn); + prepare_fixups(uprobe, &insn); + return 0; +} diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h new file mode 100644 index 000000000000..f1d13fd140f2 --- /dev/null +++ b/include/linux/uprobes.h @@ -0,0 +1,98 @@ +#ifndef _LINUX_UPROBES_H +#define _LINUX_UPROBES_H +/* + * Userspace Probes (UProbes) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * Copyright (C) IBM Corporation, 2008-2011 + * Authors: + * Srikar Dronamraju + * Jim Keniston + */ + +#include +#include + +struct vm_area_struct; +#ifdef CONFIG_ARCH_SUPPORTS_UPROBES +#include +#else + +typedef u8 uprobe_opcode_t; +struct uprobe_arch_info {}; + +#define MAX_UINSN_BYTES 4 +#endif + +#define uprobe_opcode_sz sizeof(uprobe_opcode_t) + +/* flags that denote/change uprobes behaviour */ +/* Have a copy of original instruction */ +#define UPROBES_COPY_INSN 0x1 +/* Dont run handlers when first register/ last unregister in progress*/ +#define UPROBES_RUN_HANDLER 0x2 + +struct uprobe_consumer { + int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs); + /* + * filter is optional; If a filter exists, handler is run + * if and only if filter returns true. + */ + bool (*filter)(struct uprobe_consumer *self, struct task_struct *task); + + struct uprobe_consumer *next; +}; + +struct uprobe { + struct rb_node rb_node; /* node in the rb tree */ + atomic_t ref; + struct rw_semaphore consumer_rwsem; + struct list_head pending_list; + struct uprobe_arch_info arch_info; + struct uprobe_consumer *consumers; + struct inode *inode; /* Also hold a ref to inode */ + loff_t offset; + int flags; + u8 insn[MAX_UINSN_BYTES]; +}; + +#ifdef CONFIG_UPROBES +extern int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, + unsigned long vaddr); +extern int __weak set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, + unsigned long vaddr, bool verify); +extern bool __weak is_bkpt_insn(uprobe_opcode_t *insn); +extern int register_uprobe(struct inode *inode, loff_t offset, + struct uprobe_consumer *consumer); +extern void unregister_uprobe(struct inode *inode, loff_t offset, + struct uprobe_consumer *consumer); +extern int mmap_uprobe(struct vm_area_struct *vma); +#else /* CONFIG_UPROBES is not defined */ +static inline int register_uprobe(struct inode *inode, loff_t offset, + struct uprobe_consumer *consumer) +{ + return -ENOSYS; +} +static inline void unregister_uprobe(struct inode *inode, loff_t offset, + struct uprobe_consumer *consumer) +{ +} +static inline int mmap_uprobe(struct vm_area_struct *vma) +{ + return 0; +} +#endif /* CONFIG_UPROBES */ +#endif /* _LINUX_UPROBES_H */ diff --git a/kernel/Makefile b/kernel/Makefile index 2d9de86b7e76..8609dd3d875a 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -107,6 +107,7 @@ obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o obj-$(CONFIG_PADATA) += padata.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o +obj-$(CONFIG_UPROBES) += uprobes.o $(obj)/configs.o: $(obj)/config_data.h diff --git a/kernel/uprobes.c b/kernel/uprobes.c new file mode 100644 index 000000000000..72e8bb3b52cd --- /dev/null +++ b/kernel/uprobes.c @@ -0,0 +1,976 @@ +/* + * Userspace Probes (UProbes) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * Copyright (C) IBM Corporation, 2008-2011 + * Authors: + * Srikar Dronamraju + * Jim Keniston + */ + +#include +#include +#include /* read_mapping_page */ +#include +#include +#include /* anon_vma_prepare */ +#include /* set_pte_at_notify */ +#include /* try_to_free_swap */ +#include + +static struct rb_root uprobes_tree = RB_ROOT; +static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */ + +#define UPROBES_HASH_SZ 13 +/* serialize (un)register */ +static struct mutex uprobes_mutex[UPROBES_HASH_SZ]; +#define uprobes_hash(v) (&uprobes_mutex[((unsigned long)(v)) %\ + UPROBES_HASH_SZ]) + +/* serialize uprobe->pending_list */ +static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ]; +#define uprobes_mmap_hash(v) (&uprobes_mmap_mutex[((unsigned long)(v)) %\ + UPROBES_HASH_SZ]) + +/* + * uprobe_events allows us to skip the mmap_uprobe if there are no uprobe + * events active at this time. Probably a fine grained per inode count is + * better? + */ +static atomic_t uprobe_events = ATOMIC_INIT(0); + +/* + * Maintain a temporary per vma info that can be used to search if a vma + * has already been handled. This structure is introduced since extending + * vm_area_struct wasnt recommended. + */ +struct vma_info { + struct list_head probe_list; + struct mm_struct *mm; + loff_t vaddr; +}; + +/* + * valid_vma: Verify if the specified vma is an executable vma + * Relax restrictions while unregistering: vm_flags might have + * changed after breakpoint was inserted. + * - is_register: indicates if we are in register context. + * - Return 1 if the specified virtual address is in an + * executable vma. + */ +static bool valid_vma(struct vm_area_struct *vma, bool is_register) +{ + if (!vma->vm_file) + return false; + + if (!is_register) + return true; + + if ((vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)) == + (VM_READ|VM_EXEC)) + return true; + + return false; +} + +static loff_t vma_address(struct vm_area_struct *vma, loff_t offset) +{ + loff_t vaddr; + + vaddr = vma->vm_start + offset; + vaddr -= vma->vm_pgoff << PAGE_SHIFT; + return vaddr; +} + +/** + * __replace_page - replace page in vma by new page. + * based on replace_page in mm/ksm.c + * + * @vma: vma that holds the pte pointing to page + * @page: the cowed page we are replacing by kpage + * @kpage: the modified page we replace page by + * + * Returns 0 on success, -EFAULT on failure. + */ +static int __replace_page(struct vm_area_struct *vma, struct page *page, + struct page *kpage) +{ + struct mm_struct *mm = vma->vm_mm; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *ptep; + spinlock_t *ptl; + unsigned long addr; + int err = -EFAULT; + + addr = page_address_in_vma(page, vma); + if (addr == -EFAULT) + goto out; + + pgd = pgd_offset(mm, addr); + if (!pgd_present(*pgd)) + goto out; + + pud = pud_offset(pgd, addr); + if (!pud_present(*pud)) + goto out; + + pmd = pmd_offset(pud, addr); + if (!pmd_present(*pmd)) + goto out; + + ptep = pte_offset_map_lock(mm, pmd, addr, &ptl); + if (!ptep) + goto out; + + get_page(kpage); + page_add_new_anon_rmap(kpage, vma, addr); + + flush_cache_page(vma, addr, pte_pfn(*ptep)); + ptep_clear_flush(vma, addr, ptep); + set_pte_at_notify(mm, addr, ptep, mk_pte(kpage, vma->vm_page_prot)); + + page_remove_rmap(page); + if (!page_mapped(page)) + try_to_free_swap(page); + put_page(page); + pte_unmap_unlock(ptep, ptl); + err = 0; + +out: + return err; +} + +/** + * is_bkpt_insn - check if instruction is breakpoint instruction. + * @insn: instruction to be checked. + * Default implementation of is_bkpt_insn + * Returns true if @insn is a breakpoint instruction. + */ +bool __weak is_bkpt_insn(uprobe_opcode_t *insn) +{ + return (*insn == UPROBES_BKPT_INSN); +} + +/* + * NOTE: + * Expect the breakpoint instruction to be the smallest size instruction for + * the architecture. If an arch has variable length instruction and the + * breakpoint instruction is not of the smallest length instruction + * supported by that architecture then we need to modify read_opcode / + * write_opcode accordingly. This would never be a problem for archs that + * have fixed length instructions. + */ + +/* + * write_opcode - write the opcode at a given virtual address. + * @mm: the probed process address space. + * @uprobe: the breakpointing information. + * @vaddr: the virtual address to store the opcode. + * @opcode: opcode to be written at @vaddr. + * + * Called with mm->mmap_sem held (for read and with a reference to + * mm). + * + * For mm @mm, write the opcode at @vaddr. + * Return 0 (success) or a negative errno. + */ +static int write_opcode(struct mm_struct *mm, struct uprobe *uprobe, + unsigned long vaddr, uprobe_opcode_t opcode) +{ + struct page *old_page, *new_page; + struct address_space *mapping; + void *vaddr_old, *vaddr_new; + struct vm_area_struct *vma; + loff_t addr; + int ret; + + /* Read the page with vaddr into memory */ + ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &old_page, &vma); + if (ret <= 0) + return ret; + ret = -EINVAL; + + /* + * We are interested in text pages only. Our pages of interest + * should be mapped for read and execute only. We desist from + * adding probes in write mapped pages since the breakpoints + * might end up in the file copy. + */ + if (!valid_vma(vma, is_bkpt_insn(&opcode))) + goto put_out; + + mapping = uprobe->inode->i_mapping; + if (mapping != vma->vm_file->f_mapping) + goto put_out; + + addr = vma_address(vma, uprobe->offset); + if (vaddr != (unsigned long)addr) + goto put_out; + + ret = -ENOMEM; + new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr); + if (!new_page) + goto put_out; + + __SetPageUptodate(new_page); + + /* + * lock page will serialize against do_wp_page()'s + * PageAnon() handling + */ + lock_page(old_page); + /* copy the page now that we've got it stable */ + vaddr_old = kmap_atomic(old_page); + vaddr_new = kmap_atomic(new_page); + + memcpy(vaddr_new, vaddr_old, PAGE_SIZE); + /* poke the new insn in, ASSUMES we don't cross page boundary */ + vaddr &= ~PAGE_MASK; + BUG_ON(vaddr + uprobe_opcode_sz > PAGE_SIZE); + memcpy(vaddr_new + vaddr, &opcode, uprobe_opcode_sz); + + kunmap_atomic(vaddr_new); + kunmap_atomic(vaddr_old); + + ret = anon_vma_prepare(vma); + if (ret) + goto unlock_out; + + lock_page(new_page); + ret = __replace_page(vma, old_page, new_page); + unlock_page(new_page); + +unlock_out: + unlock_page(old_page); + page_cache_release(new_page); + +put_out: + put_page(old_page); /* we did a get_page in the beginning */ + return ret; +} + +/** + * read_opcode - read the opcode at a given virtual address. + * @mm: the probed process address space. + * @vaddr: the virtual address to read the opcode. + * @opcode: location to store the read opcode. + * + * Called with mm->mmap_sem held (for read and with a reference to + * mm. + * + * For mm @mm, read the opcode at @vaddr and store it in @opcode. + * Return 0 (success) or a negative errno. + */ +static int read_opcode(struct mm_struct *mm, unsigned long vaddr, + uprobe_opcode_t *opcode) +{ + struct page *page; + void *vaddr_new; + int ret; + + ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &page, NULL); + if (ret <= 0) + return ret; + + lock_page(page); + vaddr_new = kmap_atomic(page); + vaddr &= ~PAGE_MASK; + memcpy(opcode, vaddr_new + vaddr, uprobe_opcode_sz); + kunmap_atomic(vaddr_new); + unlock_page(page); + put_page(page); /* we did a get_user_pages in the beginning */ + return 0; +} + +static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) +{ + uprobe_opcode_t opcode; + int result = read_opcode(mm, vaddr, &opcode); + + if (result) + return result; + + if (is_bkpt_insn(&opcode)) + return 1; + + return 0; +} + +/** + * set_bkpt - store breakpoint at a given address. + * @mm: the probed process address space. + * @uprobe: the probepoint information. + * @vaddr: the virtual address to insert the opcode. + * + * For mm @mm, store the breakpoint instruction at @vaddr. + * Return 0 (success) or a negative errno. + */ +int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, + unsigned long vaddr) +{ + int result = is_bkpt_at_addr(mm, vaddr); + + if (result == 1) + return -EEXIST; + + if (result) + return result; + + return write_opcode(mm, uprobe, vaddr, UPROBES_BKPT_INSN); +} + +/** + * set_orig_insn - Restore the original instruction. + * @mm: the probed process address space. + * @uprobe: the probepoint information. + * @vaddr: the virtual address to insert the opcode. + * @verify: if true, verify existance of breakpoint instruction. + * + * For mm @mm, restore the original opcode (opcode) at @vaddr. + * Return 0 (success) or a negative errno. + */ +int __weak set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, + unsigned long vaddr, bool verify) +{ + if (verify) { + int result = is_bkpt_at_addr(mm, vaddr); + + if (!result) + return -EINVAL; + + if (result != 1) + return result; + } + return write_opcode(mm, uprobe, vaddr, + *(uprobe_opcode_t *)uprobe->insn); +} + +static int match_uprobe(struct uprobe *l, struct uprobe *r) +{ + if (l->inode < r->inode) + return -1; + if (l->inode > r->inode) + return 1; + else { + if (l->offset < r->offset) + return -1; + + if (l->offset > r->offset) + return 1; + } + + return 0; +} + +static struct uprobe *__find_uprobe(struct inode *inode, loff_t offset) +{ + struct uprobe u = { .inode = inode, .offset = offset }; + struct rb_node *n = uprobes_tree.rb_node; + struct uprobe *uprobe; + int match; + + while (n) { + uprobe = rb_entry(n, struct uprobe, rb_node); + match = match_uprobe(&u, uprobe); + if (!match) { + atomic_inc(&uprobe->ref); + return uprobe; + } + if (match < 0) + n = n->rb_left; + else + n = n->rb_right; + } + return NULL; +} + +/* + * Find a uprobe corresponding to a given inode:offset + * Acquires uprobes_treelock + */ +static struct uprobe *find_uprobe(struct inode *inode, loff_t offset) +{ + struct uprobe *uprobe; + unsigned long flags; + + spin_lock_irqsave(&uprobes_treelock, flags); + uprobe = __find_uprobe(inode, offset); + spin_unlock_irqrestore(&uprobes_treelock, flags); + return uprobe; +} + +static struct uprobe *__insert_uprobe(struct uprobe *uprobe) +{ + struct rb_node **p = &uprobes_tree.rb_node; + struct rb_node *parent = NULL; + struct uprobe *u; + int match; + + while (*p) { + parent = *p; + u = rb_entry(parent, struct uprobe, rb_node); + match = match_uprobe(uprobe, u); + if (!match) { + atomic_inc(&u->ref); + return u; + } + + if (match < 0) + p = &parent->rb_left; + else + p = &parent->rb_right; + + } + u = NULL; + rb_link_node(&uprobe->rb_node, parent, p); + rb_insert_color(&uprobe->rb_node, &uprobes_tree); + /* get access + creation ref */ + atomic_set(&uprobe->ref, 2); + return u; +} + +/* + * Acquires uprobes_treelock. + * Matching uprobe already exists in rbtree; + * increment (access refcount) and return the matching uprobe. + * + * No matching uprobe; insert the uprobe in rb_tree; + * get a double refcount (access + creation) and return NULL. + */ +static struct uprobe *insert_uprobe(struct uprobe *uprobe) +{ + unsigned long flags; + struct uprobe *u; + + spin_lock_irqsave(&uprobes_treelock, flags); + u = __insert_uprobe(uprobe); + spin_unlock_irqrestore(&uprobes_treelock, flags); + return u; +} + +static void put_uprobe(struct uprobe *uprobe) +{ + if (atomic_dec_and_test(&uprobe->ref)) + kfree(uprobe); +} + +static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) +{ + struct uprobe *uprobe, *cur_uprobe; + + uprobe = kzalloc(sizeof(struct uprobe), GFP_KERNEL); + if (!uprobe) + return NULL; + + uprobe->inode = igrab(inode); + uprobe->offset = offset; + init_rwsem(&uprobe->consumer_rwsem); + INIT_LIST_HEAD(&uprobe->pending_list); + + /* add to uprobes_tree, sorted on inode:offset */ + cur_uprobe = insert_uprobe(uprobe); + + /* a uprobe exists for this inode:offset combination */ + if (cur_uprobe) { + kfree(uprobe); + uprobe = cur_uprobe; + iput(inode); + } else + atomic_inc(&uprobe_events); + return uprobe; +} + +/* Returns the previous consumer */ +static struct uprobe_consumer *add_consumer(struct uprobe *uprobe, + struct uprobe_consumer *consumer) +{ + down_write(&uprobe->consumer_rwsem); + consumer->next = uprobe->consumers; + uprobe->consumers = consumer; + up_write(&uprobe->consumer_rwsem); + return consumer->next; +} + +/* + * For uprobe @uprobe, delete the consumer @consumer. + * Return true if the @consumer is deleted successfully + * or return false. + */ +static bool del_consumer(struct uprobe *uprobe, + struct uprobe_consumer *consumer) +{ + struct uprobe_consumer **con; + bool ret = false; + + down_write(&uprobe->consumer_rwsem); + for (con = &uprobe->consumers; *con; con = &(*con)->next) { + if (*con == consumer) { + *con = consumer->next; + ret = true; + break; + } + } + up_write(&uprobe->consumer_rwsem); + return ret; +} + +static int __copy_insn(struct address_space *mapping, + struct vm_area_struct *vma, char *insn, + unsigned long nbytes, unsigned long offset) +{ + struct file *filp = vma->vm_file; + struct page *page; + void *vaddr; + unsigned long off1; + unsigned long idx; + + if (!filp) + return -EINVAL; + + idx = (unsigned long)(offset >> PAGE_CACHE_SHIFT); + off1 = offset &= ~PAGE_MASK; + + /* + * Ensure that the page that has the original instruction is + * populated and in page-cache. + */ + page = read_mapping_page(mapping, idx, filp); + if (IS_ERR(page)) + return PTR_ERR(page); + + vaddr = kmap_atomic(page); + memcpy(insn, vaddr + off1, nbytes); + kunmap_atomic(vaddr); + page_cache_release(page); + return 0; +} + +static int copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, + unsigned long addr) +{ + struct address_space *mapping; + int bytes; + unsigned long nbytes; + + addr &= ~PAGE_MASK; + nbytes = PAGE_SIZE - addr; + mapping = uprobe->inode->i_mapping; + + /* Instruction at end of binary; copy only available bytes */ + if (uprobe->offset + MAX_UINSN_BYTES > uprobe->inode->i_size) + bytes = uprobe->inode->i_size - uprobe->offset; + else + bytes = MAX_UINSN_BYTES; + + /* Instruction at the page-boundary; copy bytes in second page */ + if (nbytes < bytes) { + if (__copy_insn(mapping, vma, uprobe->insn + nbytes, + bytes - nbytes, uprobe->offset + nbytes)) + return -ENOMEM; + + bytes = nbytes; + } + return __copy_insn(mapping, vma, uprobe->insn, bytes, uprobe->offset); +} + +static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, + struct vm_area_struct *vma, loff_t vaddr) +{ + unsigned long addr; + int ret; + + /* + * If probe is being deleted, unregister thread could be done with + * the vma-rmap-walk through. Adding a probe now can be fatal since + * nobody will be able to cleanup. Also we could be from fork or + * mremap path, where the probe might have already been inserted. + * Hence behave as if probe already existed. + */ + if (!uprobe->consumers) + return -EEXIST; + + addr = (unsigned long)vaddr; + if (!(uprobe->flags & UPROBES_COPY_INSN)) { + ret = copy_insn(uprobe, vma, addr); + if (ret) + return ret; + + if (is_bkpt_insn((uprobe_opcode_t *)uprobe->insn)) + return -EEXIST; + + ret = analyze_insn(mm, uprobe); + if (ret) + return ret; + + uprobe->flags |= UPROBES_COPY_INSN; + } + ret = set_bkpt(mm, uprobe, addr); + + return ret; +} + +static void remove_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, + loff_t vaddr) +{ + set_orig_insn(mm, uprobe, (unsigned long)vaddr, true); +} + +static void delete_uprobe(struct uprobe *uprobe) +{ + unsigned long flags; + + spin_lock_irqsave(&uprobes_treelock, flags); + rb_erase(&uprobe->rb_node, &uprobes_tree); + spin_unlock_irqrestore(&uprobes_treelock, flags); + iput(uprobe->inode); + put_uprobe(uprobe); + atomic_dec(&uprobe_events); +} + +static struct vma_info *__find_next_vma_info(struct list_head *head, + loff_t offset, struct address_space *mapping, + struct vma_info *vi, bool is_register) +{ + struct prio_tree_iter iter; + struct vm_area_struct *vma; + struct vma_info *tmpvi; + loff_t vaddr; + unsigned long pgoff = offset >> PAGE_SHIFT; + int existing_vma; + + vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { + if (!valid_vma(vma, is_register)) + continue; + + existing_vma = 0; + vaddr = vma_address(vma, offset); + list_for_each_entry(tmpvi, head, probe_list) { + if (tmpvi->mm == vma->vm_mm && tmpvi->vaddr == vaddr) { + existing_vma = 1; + break; + } + } + + /* + * Another vma needs a probe to be installed. However skip + * installing the probe if the vma is about to be unlinked. + */ + if (!existing_vma && + atomic_inc_not_zero(&vma->vm_mm->mm_users)) { + vi->mm = vma->vm_mm; + vi->vaddr = vaddr; + list_add(&vi->probe_list, head); + return vi; + } + } + return NULL; +} + +/* + * Iterate in the rmap prio tree and find a vma where a probe has not + * yet been inserted. + */ +static struct vma_info *find_next_vma_info(struct list_head *head, + loff_t offset, struct address_space *mapping, + bool is_register) +{ + struct vma_info *vi, *retvi; + vi = kzalloc(sizeof(struct vma_info), GFP_KERNEL); + if (!vi) + return ERR_PTR(-ENOMEM); + + mutex_lock(&mapping->i_mmap_mutex); + retvi = __find_next_vma_info(head, offset, mapping, vi, is_register); + mutex_unlock(&mapping->i_mmap_mutex); + + if (!retvi) + kfree(vi); + return retvi; +} + +static int register_for_each_vma(struct uprobe *uprobe, bool is_register) +{ + struct list_head try_list; + struct vm_area_struct *vma; + struct address_space *mapping; + struct vma_info *vi, *tmpvi; + struct mm_struct *mm; + loff_t vaddr; + int ret = 0; + + mapping = uprobe->inode->i_mapping; + INIT_LIST_HEAD(&try_list); + while ((vi = find_next_vma_info(&try_list, uprobe->offset, + mapping, is_register)) != NULL) { + if (IS_ERR(vi)) { + ret = PTR_ERR(vi); + break; + } + mm = vi->mm; + down_read(&mm->mmap_sem); + vma = find_vma(mm, (unsigned long)vi->vaddr); + if (!vma || !valid_vma(vma, is_register)) { + list_del(&vi->probe_list); + kfree(vi); + up_read(&mm->mmap_sem); + mmput(mm); + continue; + } + vaddr = vma_address(vma, uprobe->offset); + if (vma->vm_file->f_mapping->host != uprobe->inode || + vaddr != vi->vaddr) { + list_del(&vi->probe_list); + kfree(vi); + up_read(&mm->mmap_sem); + mmput(mm); + continue; + } + + if (is_register) + ret = install_breakpoint(mm, uprobe, vma, vi->vaddr); + else + remove_breakpoint(mm, uprobe, vi->vaddr); + + up_read(&mm->mmap_sem); + mmput(mm); + if (is_register) { + if (ret && ret == -EEXIST) + ret = 0; + if (ret) + break; + } + } + list_for_each_entry_safe(vi, tmpvi, &try_list, probe_list) { + list_del(&vi->probe_list); + kfree(vi); + } + return ret; +} + +static int __register_uprobe(struct uprobe *uprobe) +{ + return register_for_each_vma(uprobe, true); +} + +static void __unregister_uprobe(struct uprobe *uprobe) +{ + if (!register_for_each_vma(uprobe, false)) + delete_uprobe(uprobe); + + /* TODO : cant unregister? schedule a worker thread */ +} + +/* + * register_uprobe - register a probe + * @inode: the file in which the probe has to be placed. + * @offset: offset from the start of the file. + * @consumer: information on howto handle the probe.. + * + * Apart from the access refcount, register_uprobe() takes a creation + * refcount (thro alloc_uprobe) if and only if this @uprobe is getting + * inserted into the rbtree (i.e first consumer for a @inode:@offset + * tuple). Creation refcount stops unregister_uprobe from freeing the + * @uprobe even before the register operation is complete. Creation + * refcount is released when the last @consumer for the @uprobe + * unregisters. + * + * Return errno if it cannot successully install probes + * else return 0 (success) + */ +int register_uprobe(struct inode *inode, loff_t offset, + struct uprobe_consumer *consumer) +{ + struct uprobe *uprobe; + int ret = -EINVAL; + + if (!inode || !consumer || consumer->next) + return ret; + + if (offset > i_size_read(inode)) + return ret; + + ret = 0; + mutex_lock(uprobes_hash(inode)); + uprobe = alloc_uprobe(inode, offset); + if (uprobe && !add_consumer(uprobe, consumer)) { + ret = __register_uprobe(uprobe); + if (ret) { + uprobe->consumers = NULL; + __unregister_uprobe(uprobe); + } else + uprobe->flags |= UPROBES_RUN_HANDLER; + } + + mutex_unlock(uprobes_hash(inode)); + put_uprobe(uprobe); + + return ret; +} + +/* + * unregister_uprobe - unregister a already registered probe. + * @inode: the file in which the probe has to be removed. + * @offset: offset from the start of the file. + * @consumer: identify which probe if multiple probes are colocated. + */ +void unregister_uprobe(struct inode *inode, loff_t offset, + struct uprobe_consumer *consumer) +{ + struct uprobe *uprobe = NULL; + + if (!inode || !consumer) + return; + + uprobe = find_uprobe(inode, offset); + if (!uprobe) + return; + + mutex_lock(uprobes_hash(inode)); + if (!del_consumer(uprobe, consumer)) + goto unreg_out; + + if (!uprobe->consumers) { + __unregister_uprobe(uprobe); + uprobe->flags &= ~UPROBES_RUN_HANDLER; + } + +unreg_out: + mutex_unlock(uprobes_hash(inode)); + if (uprobe) + put_uprobe(uprobe); +} + +/* + * Of all the nodes that correspond to the given inode, return the node + * with the least offset. + */ +static struct rb_node *find_least_offset_node(struct inode *inode) +{ + struct uprobe u = { .inode = inode, .offset = 0}; + struct rb_node *n = uprobes_tree.rb_node; + struct rb_node *close_node = NULL; + struct uprobe *uprobe; + int match; + + while (n) { + uprobe = rb_entry(n, struct uprobe, rb_node); + match = match_uprobe(&u, uprobe); + if (uprobe->inode == inode) + close_node = n; + + if (!match) + return close_node; + + if (match < 0) + n = n->rb_left; + else + n = n->rb_right; + } + return close_node; +} + +/* + * For a given inode, build a list of probes that need to be inserted. + */ +static void build_probe_list(struct inode *inode, struct list_head *head) +{ + struct uprobe *uprobe; + struct rb_node *n; + unsigned long flags; + + spin_lock_irqsave(&uprobes_treelock, flags); + n = find_least_offset_node(inode); + for (; n; n = rb_next(n)) { + uprobe = rb_entry(n, struct uprobe, rb_node); + if (uprobe->inode != inode) + break; + + list_add(&uprobe->pending_list, head); + atomic_inc(&uprobe->ref); + } + spin_unlock_irqrestore(&uprobes_treelock, flags); +} + +/* + * Called from mmap_region. + * called with mm->mmap_sem acquired. + * + * Return -ve no if we fail to insert probes and we cannot + * bail-out. + * Return 0 otherwise. i.e : + * - successful insertion of probes + * - (or) no possible probes to be inserted. + * - (or) insertion of probes failed but we can bail-out. + */ +int mmap_uprobe(struct vm_area_struct *vma) +{ + struct list_head tmp_list; + struct uprobe *uprobe, *u; + struct inode *inode; + int ret = 0; + + if (!atomic_read(&uprobe_events) || !valid_vma(vma, true)) + return ret; /* Bail-out */ + + inode = vma->vm_file->f_mapping->host; + if (!inode) + return ret; + + INIT_LIST_HEAD(&tmp_list); + mutex_lock(uprobes_mmap_hash(inode)); + build_probe_list(inode, &tmp_list); + list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { + loff_t vaddr; + + list_del(&uprobe->pending_list); + if (!ret) { + vaddr = vma_address(vma, uprobe->offset); + if (vaddr < vma->vm_start || vaddr >= vma->vm_end) { + put_uprobe(uprobe); + continue; + } + ret = install_breakpoint(vma->vm_mm, uprobe, vma, + vaddr); + if (ret == -EEXIST) + ret = 0; + } + put_uprobe(uprobe); + } + + mutex_unlock(uprobes_mmap_hash(inode)); + + return ret; +} + +static int __init init_uprobes(void) +{ + int i; + + for (i = 0; i < UPROBES_HASH_SZ; i++) { + mutex_init(&uprobes_mutex[i]); + mutex_init(&uprobes_mmap_mutex[i]); + } + return 0; +} + +static void __exit exit_uprobes(void) +{ +} + +module_init(init_uprobes); +module_exit(exit_uprobes); diff --git a/mm/mmap.c b/mm/mmap.c index 3f758c7f4c81..1aed183636d7 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -616,6 +617,13 @@ again: remove_next = 1 + (end > next->vm_end); if (mapping) mutex_unlock(&mapping->i_mmap_mutex); + if (root) { + mmap_uprobe(vma); + + if (adjust_next) + mmap_uprobe(next); + } + if (remove_next) { if (file) { fput(file); @@ -637,6 +645,8 @@ again: remove_next = 1 + (end > next->vm_end); goto again; } } + if (insert && file) + mmap_uprobe(insert); validate_mm(mm); @@ -1329,6 +1339,11 @@ out: mm->locked_vm += (len >> PAGE_SHIFT); } else if ((flags & MAP_POPULATE) && !(flags & MAP_NONBLOCK)) make_pages_present(addr, addr + len); + + if (file && mmap_uprobe(vma)) + /* matching probes but cannot insert */ + goto unmap_and_free_vma; + return addr; unmap_and_free_vma: @@ -2285,6 +2300,10 @@ int insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma) if ((vma->vm_flags & VM_ACCOUNT) && security_vm_enough_memory_mm(mm, vma_pages(vma))) return -ENOMEM; + + if (vma->vm_file && mmap_uprobe(vma)) + return -EINVAL; + vma_link(mm, vma, prev, rb_link, rb_parent); return 0; } @@ -2354,6 +2373,10 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, new_vma->vm_pgoff = pgoff; if (new_vma->vm_file) { get_file(new_vma->vm_file); + + if (mmap_uprobe(new_vma)) + goto out_free_mempol; + if (vma->vm_flags & VM_EXECUTABLE) added_exe_file_vma(mm); } -- cgit v1.2.3 From 7b2d81d48a2d8e37efb6ce7b4d5ef58822b30d89 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 17 Feb 2012 09:27:41 +0100 Subject: uprobes/core: Clean up, refactor and improve the code Make the uprobes code readable to me: - improve the Kconfig text so that a mere mortal gets some idea what CONFIG_UPROBES=y is really about - do trivial renames to standardize around the uprobes_*() namespace - clean up and simplify various code flow details - separate basic blocks of functionality - line break artifact and white space related removal - use standard local varible definition blocks - use vertical spacing to make things more readable - remove unnecessary volatile - restructure comment blocks to make them more uniform and more readable in general Cc: Srikar Dronamraju Cc: Jim Keniston Cc: Peter Zijlstra Cc: Oleg Nesterov Cc: Masami Hiramatsu Cc: Arnaldo Carvalho de Melo Cc: Anton Arapov Cc: Ananth N Mavinakayanahalli Link: http://lkml.kernel.org/n/tip-ewbwhb8o6navvllsauu7k07p@git.kernel.org Signed-off-by: Ingo Molnar --- arch/Kconfig | 14 ++- arch/x86/include/asm/uprobes.h | 17 ++-- arch/x86/kernel/uprobes.c | 129 ++++++++++++------------ include/linux/uprobes.h | 28 +++--- kernel/uprobes.c | 219 ++++++++++++++++++++++++----------------- mm/mmap.c | 12 +-- 6 files changed, 233 insertions(+), 186 deletions(-) (limited to 'arch') diff --git a/arch/Kconfig b/arch/Kconfig index 284f5898f526..cca5b545d806 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -66,13 +66,19 @@ config OPTPROBES depends on !PREEMPT config UPROBES - bool "User-space probes (EXPERIMENTAL)" + bool "Transparent user-space probes (EXPERIMENTAL)" depends on ARCH_SUPPORTS_UPROBES default n help - Uprobes enables kernel subsystems to establish probepoints - in user applications and execute handler functions when - the probepoints are hit. + Uprobes is the user-space counterpart to kprobes: they + enable instrumentation applications (such as 'perf probe') + to establish unintrusive probes in user-space binaries and + libraries, by executing handler functions when the probes + are hit by user-space applications. + + ( These probes come in the form of single-byte breakpoints, + managed by the kernel and kept transparent to the probed + application. ) If in doubt, say "N". diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index 8208234391ff..072df3902636 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h @@ -1,7 +1,7 @@ #ifndef _ASM_UPROBES_H #define _ASM_UPROBES_H /* - * Userspace Probes (UProbes) for x86 + * User-space Probes (UProbes) for x86 * * 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 @@ -24,19 +24,20 @@ */ typedef u8 uprobe_opcode_t; -#define MAX_UINSN_BYTES 16 -#define UPROBES_XOL_SLOT_BYTES 128 /* to keep it cache aligned */ -#define UPROBES_BKPT_INSN 0xcc -#define UPROBES_BKPT_INSN_SIZE 1 +#define MAX_UINSN_BYTES 16 +#define UPROBES_XOL_SLOT_BYTES 128 /* to keep it cache aligned */ + +#define UPROBES_BKPT_INSN 0xcc +#define UPROBES_BKPT_INSN_SIZE 1 struct uprobe_arch_info { - u16 fixups; + u16 fixups; #ifdef CONFIG_X86_64 - unsigned long rip_rela_target_address; + unsigned long rip_rela_target_address; #endif }; struct uprobe; -extern int analyze_insn(struct mm_struct *mm, struct uprobe *uprobe); +extern int arch_uprobes_analyze_insn(struct mm_struct *mm, struct uprobe *uprobe); #endif /* _ASM_UPROBES_H */ diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 2a301bb91bdb..cf2a18498425 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -1,5 +1,5 @@ /* - * Userspace Probes (UProbes) for x86 + * User-space Probes (UProbes) for x86 * * 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 @@ -20,7 +20,6 @@ * Srikar Dronamraju * Jim Keniston */ - #include #include #include @@ -42,10 +41,10 @@ #define UPROBES_FIX_RIP_CX 0x4000 /* Adaptations for mhiramat x86 decoder v14. */ -#define OPCODE1(insn) ((insn)->opcode.bytes[0]) -#define OPCODE2(insn) ((insn)->opcode.bytes[1]) -#define OPCODE3(insn) ((insn)->opcode.bytes[2]) -#define MODRM_REG(insn) X86_MODRM_REG(insn->modrm.value) +#define OPCODE1(insn) ((insn)->opcode.bytes[0]) +#define OPCODE2(insn) ((insn)->opcode.bytes[1]) +#define OPCODE3(insn) ((insn)->opcode.bytes[2]) +#define MODRM_REG(insn) X86_MODRM_REG(insn->modrm.value) #define W(row, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf)\ (((b0##UL << 0x0)|(b1##UL << 0x1)|(b2##UL << 0x2)|(b3##UL << 0x3) | \ @@ -55,7 +54,7 @@ << (row % 32)) #ifdef CONFIG_X86_64 -static volatile u32 good_insns_64[256 / 32] = { +static u32 good_insns_64[256 / 32] = { /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ /* ---------------------------------------------- */ W(0x00, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) | /* 00 */ @@ -81,7 +80,7 @@ static volatile u32 good_insns_64[256 / 32] = { /* Good-instruction tables for 32-bit apps */ -static volatile u32 good_insns_32[256 / 32] = { +static u32 good_insns_32[256 / 32] = { /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ /* ---------------------------------------------- */ W(0x00, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0) | /* 00 */ @@ -105,7 +104,7 @@ static volatile u32 good_insns_32[256 / 32] = { }; /* Using this for both 64-bit and 32-bit apps */ -static volatile u32 good_2byte_insns[256 / 32] = { +static u32 good_2byte_insns[256 / 32] = { /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ /* ---------------------------------------------- */ W(0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1) | /* 00 */ @@ -132,42 +131,47 @@ static volatile u32 good_2byte_insns[256 / 32] = { /* * opcodes we'll probably never support: - * 6c-6d, e4-e5, ec-ed - in - * 6e-6f, e6-e7, ee-ef - out - * cc, cd - int3, int - * cf - iret - * d6 - illegal instruction - * f1 - int1/icebp - * f4 - hlt - * fa, fb - cli, sti - * 0f - lar, lsl, syscall, clts, sysret, sysenter, sysexit, invd, wbinvd, ud2 + * + * 6c-6d, e4-e5, ec-ed - in + * 6e-6f, e6-e7, ee-ef - out + * cc, cd - int3, int + * cf - iret + * d6 - illegal instruction + * f1 - int1/icebp + * f4 - hlt + * fa, fb - cli, sti + * 0f - lar, lsl, syscall, clts, sysret, sysenter, sysexit, invd, wbinvd, ud2 * * invalid opcodes in 64-bit mode: - * 06, 0e, 16, 1e, 27, 2f, 37, 3f, 60-62, 82, c4-c5, d4-d5 * - * 63 - we support this opcode in x86_64 but not in i386. + * 06, 0e, 16, 1e, 27, 2f, 37, 3f, 60-62, 82, c4-c5, d4-d5 + * 63 - we support this opcode in x86_64 but not in i386. * * opcodes we may need to refine support for: - * 0f - 2-byte instructions: For many of these instructions, the validity - * depends on the prefix and/or the reg field. On such instructions, we - * just consider the opcode combination valid if it corresponds to any - * valid instruction. - * 8f - Group 1 - only reg = 0 is OK - * c6-c7 - Group 11 - only reg = 0 is OK - * d9-df - fpu insns with some illegal encodings - * f2, f3 - repnz, repz prefixes. These are also the first byte for - * certain floating-point instructions, such as addsd. - * fe - Group 4 - only reg = 0 or 1 is OK - * ff - Group 5 - only reg = 0-6 is OK + * + * 0f - 2-byte instructions: For many of these instructions, the validity + * depends on the prefix and/or the reg field. On such instructions, we + * just consider the opcode combination valid if it corresponds to any + * valid instruction. + * + * 8f - Group 1 - only reg = 0 is OK + * c6-c7 - Group 11 - only reg = 0 is OK + * d9-df - fpu insns with some illegal encodings + * f2, f3 - repnz, repz prefixes. These are also the first byte for + * certain floating-point instructions, such as addsd. + * + * fe - Group 4 - only reg = 0 or 1 is OK + * ff - Group 5 - only reg = 0-6 is OK * * others -- Do we need to support these? - * 0f - (floating-point?) prefetch instructions - * 07, 17, 1f - pop es, pop ss, pop ds - * 26, 2e, 36, 3e - es:, cs:, ss:, ds: segment prefixes -- + * + * 0f - (floating-point?) prefetch instructions + * 07, 17, 1f - pop es, pop ss, pop ds + * 26, 2e, 36, 3e - es:, cs:, ss:, ds: segment prefixes -- * but 64 and 65 (fs: and gs:) seem to be used, so we support them - * 67 - addr16 prefix - * ce - into - * f0 - lock prefix + * 67 - addr16 prefix + * ce - into + * f0 - lock prefix */ /* @@ -182,11 +186,11 @@ static bool is_prefix_bad(struct insn *insn) for (i = 0; i < insn->prefixes.nbytes; i++) { switch (insn->prefixes.bytes[i]) { - case 0x26: /*INAT_PFX_ES */ - case 0x2E: /*INAT_PFX_CS */ - case 0x36: /*INAT_PFX_DS */ - case 0x3E: /*INAT_PFX_SS */ - case 0xF0: /*INAT_PFX_LOCK */ + case 0x26: /* INAT_PFX_ES */ + case 0x2E: /* INAT_PFX_CS */ + case 0x36: /* INAT_PFX_DS */ + case 0x3E: /* INAT_PFX_SS */ + case 0xF0: /* INAT_PFX_LOCK */ return true; } } @@ -201,12 +205,15 @@ static int validate_insn_32bits(struct uprobe *uprobe, struct insn *insn) insn_get_opcode(insn); if (is_prefix_bad(insn)) return -ENOTSUPP; + if (test_bit(OPCODE1(insn), (unsigned long *)good_insns_32)) return 0; + if (insn->opcode.nbytes == 2) { if (test_bit(OPCODE2(insn), (unsigned long *)good_2byte_insns)) return 0; } + return -ENOTSUPP; } @@ -282,12 +289,12 @@ static void prepare_fixups(struct uprobe *uprobe, struct insn *insn) * disastrous. * * Some useful facts about rip-relative instructions: - * - There's always a modrm byte. - * - There's never a SIB byte. - * - The displacement is always 4 bytes. + * + * - There's always a modrm byte. + * - There's never a SIB byte. + * - The displacement is always 4 bytes. */ -static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, - struct insn *insn) +static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) { u8 *cursor; u8 reg; @@ -342,13 +349,12 @@ static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, } /* Target address = address of next instruction + (signed) offset */ - uprobe->arch_info.rip_rela_target_address = (long)insn->length - + insn->displacement.value; + uprobe->arch_info.rip_rela_target_address = (long)insn->length + insn->displacement.value; + /* Displacement field is gone; slide immediate field (if any) over. */ if (insn->immediate.nbytes) { cursor++; - memmove(cursor, cursor + insn->displacement.nbytes, - insn->immediate.nbytes); + memmove(cursor, cursor + insn->displacement.nbytes, insn->immediate.nbytes); } return; } @@ -361,8 +367,10 @@ static int validate_insn_64bits(struct uprobe *uprobe, struct insn *insn) insn_get_opcode(insn); if (is_prefix_bad(insn)) return -ENOTSUPP; + if (test_bit(OPCODE1(insn), (unsigned long *)good_insns_64)) return 0; + if (insn->opcode.nbytes == 2) { if (test_bit(OPCODE2(insn), (unsigned long *)good_2byte_insns)) return 0; @@ -370,34 +378,31 @@ static int validate_insn_64bits(struct uprobe *uprobe, struct insn *insn) return -ENOTSUPP; } -static int validate_insn_bits(struct mm_struct *mm, struct uprobe *uprobe, - struct insn *insn) +static int validate_insn_bits(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) { if (mm->context.ia32_compat) return validate_insn_32bits(uprobe, insn); return validate_insn_64bits(uprobe, insn); } -#else -static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, - struct insn *insn) +#else /* 32-bit: */ +static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) { - return; + /* No RIP-relative addressing on 32-bit */ } -static int validate_insn_bits(struct mm_struct *mm, struct uprobe *uprobe, - struct insn *insn) +static int validate_insn_bits(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) { return validate_insn_32bits(uprobe, insn); } #endif /* CONFIG_X86_64 */ /** - * analyze_insn - instruction analysis including validity and fixups. + * arch_uprobes_analyze_insn - instruction analysis including validity and fixups. * @mm: the probed address space. * @uprobe: the probepoint information. * Return 0 on success or a -ve number on error. */ -int analyze_insn(struct mm_struct *mm, struct uprobe *uprobe) +int arch_uprobes_analyze_insn(struct mm_struct *mm, struct uprobe *uprobe) { int ret; struct insn insn; @@ -406,7 +411,9 @@ int analyze_insn(struct mm_struct *mm, struct uprobe *uprobe) ret = validate_insn_bits(mm, uprobe, &insn); if (ret != 0) return ret; + handle_riprel_insn(mm, uprobe, &insn); prepare_fixups(uprobe, &insn); + return 0; } diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index f1d13fd140f2..64e45f116b2a 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -1,7 +1,7 @@ #ifndef _LINUX_UPROBES_H #define _LINUX_UPROBES_H /* - * Userspace Probes (UProbes) + * User-space Probes (UProbes) * * 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 @@ -40,8 +40,10 @@ struct uprobe_arch_info {}; #define uprobe_opcode_sz sizeof(uprobe_opcode_t) /* flags that denote/change uprobes behaviour */ + /* Have a copy of original instruction */ #define UPROBES_COPY_INSN 0x1 + /* Dont run handlers when first register/ last unregister in progress*/ #define UPROBES_RUN_HANDLER 0x2 @@ -70,27 +72,23 @@ struct uprobe { }; #ifdef CONFIG_UPROBES -extern int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, - unsigned long vaddr); -extern int __weak set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, - unsigned long vaddr, bool verify); +extern int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr); +extern int __weak set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr, bool verify); extern bool __weak is_bkpt_insn(uprobe_opcode_t *insn); -extern int register_uprobe(struct inode *inode, loff_t offset, - struct uprobe_consumer *consumer); -extern void unregister_uprobe(struct inode *inode, loff_t offset, - struct uprobe_consumer *consumer); -extern int mmap_uprobe(struct vm_area_struct *vma); +extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer); +extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer); +extern int uprobe_mmap(struct vm_area_struct *vma); #else /* CONFIG_UPROBES is not defined */ -static inline int register_uprobe(struct inode *inode, loff_t offset, - struct uprobe_consumer *consumer) +static inline int +uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) { return -ENOSYS; } -static inline void unregister_uprobe(struct inode *inode, loff_t offset, - struct uprobe_consumer *consumer) +static inline void +uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) { } -static inline int mmap_uprobe(struct vm_area_struct *vma) +static inline int uprobe_mmap(struct vm_area_struct *vma) { return 0; } diff --git a/kernel/uprobes.c b/kernel/uprobes.c index 72e8bb3b52cd..884817f1b0d3 100644 --- a/kernel/uprobes.c +++ b/kernel/uprobes.c @@ -1,5 +1,5 @@ /* - * Userspace Probes (UProbes) + * User-space Probes (UProbes) * * 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 @@ -29,24 +29,26 @@ #include /* anon_vma_prepare */ #include /* set_pte_at_notify */ #include /* try_to_free_swap */ + #include static struct rb_root uprobes_tree = RB_ROOT; + static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */ #define UPROBES_HASH_SZ 13 + /* serialize (un)register */ static struct mutex uprobes_mutex[UPROBES_HASH_SZ]; -#define uprobes_hash(v) (&uprobes_mutex[((unsigned long)(v)) %\ - UPROBES_HASH_SZ]) + +#define uprobes_hash(v) (&uprobes_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ]) /* serialize uprobe->pending_list */ static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ]; -#define uprobes_mmap_hash(v) (&uprobes_mmap_mutex[((unsigned long)(v)) %\ - UPROBES_HASH_SZ]) +#define uprobes_mmap_hash(v) (&uprobes_mmap_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ]) /* - * uprobe_events allows us to skip the mmap_uprobe if there are no uprobe + * uprobe_events allows us to skip the uprobe_mmap if there are no uprobe * events active at this time. Probably a fine grained per inode count is * better? */ @@ -58,9 +60,9 @@ static atomic_t uprobe_events = ATOMIC_INIT(0); * vm_area_struct wasnt recommended. */ struct vma_info { - struct list_head probe_list; - struct mm_struct *mm; - loff_t vaddr; + struct list_head probe_list; + struct mm_struct *mm; + loff_t vaddr; }; /* @@ -79,8 +81,7 @@ static bool valid_vma(struct vm_area_struct *vma, bool is_register) if (!is_register) return true; - if ((vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)) == - (VM_READ|VM_EXEC)) + if ((vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)) == (VM_READ|VM_EXEC)) return true; return false; @@ -92,6 +93,7 @@ static loff_t vma_address(struct vm_area_struct *vma, loff_t offset) vaddr = vma->vm_start + offset; vaddr -= vma->vm_pgoff << PAGE_SHIFT; + return vaddr; } @@ -105,8 +107,7 @@ static loff_t vma_address(struct vm_area_struct *vma, loff_t offset) * * Returns 0 on success, -EFAULT on failure. */ -static int __replace_page(struct vm_area_struct *vma, struct page *page, - struct page *kpage) +static int __replace_page(struct vm_area_struct *vma, struct page *page, struct page *kpage) { struct mm_struct *mm = vma->vm_mm; pgd_t *pgd; @@ -163,7 +164,7 @@ out: */ bool __weak is_bkpt_insn(uprobe_opcode_t *insn) { - return (*insn == UPROBES_BKPT_INSN); + return *insn == UPROBES_BKPT_INSN; } /* @@ -203,6 +204,7 @@ static int write_opcode(struct mm_struct *mm, struct uprobe *uprobe, ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &old_page, &vma); if (ret <= 0) return ret; + ret = -EINVAL; /* @@ -239,6 +241,7 @@ static int write_opcode(struct mm_struct *mm, struct uprobe *uprobe, vaddr_new = kmap_atomic(new_page); memcpy(vaddr_new, vaddr_old, PAGE_SIZE); + /* poke the new insn in, ASSUMES we don't cross page boundary */ vaddr &= ~PAGE_MASK; BUG_ON(vaddr + uprobe_opcode_sz > PAGE_SIZE); @@ -260,7 +263,8 @@ unlock_out: page_cache_release(new_page); put_out: - put_page(old_page); /* we did a get_page in the beginning */ + put_page(old_page); + return ret; } @@ -276,8 +280,7 @@ put_out: * For mm @mm, read the opcode at @vaddr and store it in @opcode. * Return 0 (success) or a negative errno. */ -static int read_opcode(struct mm_struct *mm, unsigned long vaddr, - uprobe_opcode_t *opcode) +static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t *opcode) { struct page *page; void *vaddr_new; @@ -293,15 +296,18 @@ static int read_opcode(struct mm_struct *mm, unsigned long vaddr, memcpy(opcode, vaddr_new + vaddr, uprobe_opcode_sz); kunmap_atomic(vaddr_new); unlock_page(page); - put_page(page); /* we did a get_user_pages in the beginning */ + + put_page(page); + return 0; } static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) { uprobe_opcode_t opcode; - int result = read_opcode(mm, vaddr, &opcode); + int result; + result = read_opcode(mm, vaddr, &opcode); if (result) return result; @@ -320,11 +326,11 @@ static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) * For mm @mm, store the breakpoint instruction at @vaddr. * Return 0 (success) or a negative errno. */ -int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, - unsigned long vaddr) +int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr) { - int result = is_bkpt_at_addr(mm, vaddr); + int result; + result = is_bkpt_at_addr(mm, vaddr); if (result == 1) return -EEXIST; @@ -344,35 +350,35 @@ int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, * For mm @mm, restore the original opcode (opcode) at @vaddr. * Return 0 (success) or a negative errno. */ -int __weak set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, - unsigned long vaddr, bool verify) +int __weak +set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr, bool verify) { if (verify) { - int result = is_bkpt_at_addr(mm, vaddr); + int result; + result = is_bkpt_at_addr(mm, vaddr); if (!result) return -EINVAL; if (result != 1) return result; } - return write_opcode(mm, uprobe, vaddr, - *(uprobe_opcode_t *)uprobe->insn); + return write_opcode(mm, uprobe, vaddr, *(uprobe_opcode_t *)uprobe->insn); } static int match_uprobe(struct uprobe *l, struct uprobe *r) { if (l->inode < r->inode) return -1; + if (l->inode > r->inode) return 1; - else { - if (l->offset < r->offset) - return -1; - if (l->offset > r->offset) - return 1; - } + if (l->offset < r->offset) + return -1; + + if (l->offset > r->offset) + return 1; return 0; } @@ -391,6 +397,7 @@ static struct uprobe *__find_uprobe(struct inode *inode, loff_t offset) atomic_inc(&uprobe->ref); return uprobe; } + if (match < 0) n = n->rb_left; else @@ -411,6 +418,7 @@ static struct uprobe *find_uprobe(struct inode *inode, loff_t offset) spin_lock_irqsave(&uprobes_treelock, flags); uprobe = __find_uprobe(inode, offset); spin_unlock_irqrestore(&uprobes_treelock, flags); + return uprobe; } @@ -436,16 +444,18 @@ static struct uprobe *__insert_uprobe(struct uprobe *uprobe) p = &parent->rb_right; } + u = NULL; rb_link_node(&uprobe->rb_node, parent, p); rb_insert_color(&uprobe->rb_node, &uprobes_tree); /* get access + creation ref */ atomic_set(&uprobe->ref, 2); + return u; } /* - * Acquires uprobes_treelock. + * Acquire uprobes_treelock. * Matching uprobe already exists in rbtree; * increment (access refcount) and return the matching uprobe. * @@ -460,6 +470,7 @@ static struct uprobe *insert_uprobe(struct uprobe *uprobe) spin_lock_irqsave(&uprobes_treelock, flags); u = __insert_uprobe(uprobe); spin_unlock_irqrestore(&uprobes_treelock, flags); + return u; } @@ -490,19 +501,22 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) kfree(uprobe); uprobe = cur_uprobe; iput(inode); - } else + } else { atomic_inc(&uprobe_events); + } + return uprobe; } /* Returns the previous consumer */ -static struct uprobe_consumer *add_consumer(struct uprobe *uprobe, - struct uprobe_consumer *consumer) +static struct uprobe_consumer * +consumer_add(struct uprobe *uprobe, struct uprobe_consumer *consumer) { down_write(&uprobe->consumer_rwsem); consumer->next = uprobe->consumers; uprobe->consumers = consumer; up_write(&uprobe->consumer_rwsem); + return consumer->next; } @@ -511,8 +525,7 @@ static struct uprobe_consumer *add_consumer(struct uprobe *uprobe, * Return true if the @consumer is deleted successfully * or return false. */ -static bool del_consumer(struct uprobe *uprobe, - struct uprobe_consumer *consumer) +static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *consumer) { struct uprobe_consumer **con; bool ret = false; @@ -526,6 +539,7 @@ static bool del_consumer(struct uprobe *uprobe, } } up_write(&uprobe->consumer_rwsem); + return ret; } @@ -557,15 +571,15 @@ static int __copy_insn(struct address_space *mapping, memcpy(insn, vaddr + off1, nbytes); kunmap_atomic(vaddr); page_cache_release(page); + return 0; } -static int copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, - unsigned long addr) +static int copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr) { struct address_space *mapping; - int bytes; unsigned long nbytes; + int bytes; addr &= ~PAGE_MASK; nbytes = PAGE_SIZE - addr; @@ -605,6 +619,7 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, return -EEXIST; addr = (unsigned long)vaddr; + if (!(uprobe->flags & UPROBES_COPY_INSN)) { ret = copy_insn(uprobe, vma, addr); if (ret) @@ -613,7 +628,7 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, if (is_bkpt_insn((uprobe_opcode_t *)uprobe->insn)) return -EEXIST; - ret = analyze_insn(mm, uprobe); + ret = arch_uprobes_analyze_insn(mm, uprobe); if (ret) return ret; @@ -624,8 +639,7 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, return ret; } -static void remove_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, - loff_t vaddr) +static void remove_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, loff_t vaddr) { set_orig_insn(mm, uprobe, (unsigned long)vaddr, true); } @@ -649,9 +663,11 @@ static struct vma_info *__find_next_vma_info(struct list_head *head, struct prio_tree_iter iter; struct vm_area_struct *vma; struct vma_info *tmpvi; - loff_t vaddr; - unsigned long pgoff = offset >> PAGE_SHIFT; + unsigned long pgoff; int existing_vma; + loff_t vaddr; + + pgoff = offset >> PAGE_SHIFT; vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { if (!valid_vma(vma, is_register)) @@ -659,6 +675,7 @@ static struct vma_info *__find_next_vma_info(struct list_head *head, existing_vma = 0; vaddr = vma_address(vma, offset); + list_for_each_entry(tmpvi, head, probe_list) { if (tmpvi->mm == vma->vm_mm && tmpvi->vaddr == vaddr) { existing_vma = 1; @@ -670,14 +687,15 @@ static struct vma_info *__find_next_vma_info(struct list_head *head, * Another vma needs a probe to be installed. However skip * installing the probe if the vma is about to be unlinked. */ - if (!existing_vma && - atomic_inc_not_zero(&vma->vm_mm->mm_users)) { + if (!existing_vma && atomic_inc_not_zero(&vma->vm_mm->mm_users)) { vi->mm = vma->vm_mm; vi->vaddr = vaddr; list_add(&vi->probe_list, head); + return vi; } } + return NULL; } @@ -685,11 +703,12 @@ static struct vma_info *__find_next_vma_info(struct list_head *head, * Iterate in the rmap prio tree and find a vma where a probe has not * yet been inserted. */ -static struct vma_info *find_next_vma_info(struct list_head *head, - loff_t offset, struct address_space *mapping, - bool is_register) +static struct vma_info * +find_next_vma_info(struct list_head *head, loff_t offset, struct address_space *mapping, + bool is_register) { struct vma_info *vi, *retvi; + vi = kzalloc(sizeof(struct vma_info), GFP_KERNEL); if (!vi) return ERR_PTR(-ENOMEM); @@ -700,6 +719,7 @@ static struct vma_info *find_next_vma_info(struct list_head *head, if (!retvi) kfree(vi); + return retvi; } @@ -711,16 +731,23 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register) struct vma_info *vi, *tmpvi; struct mm_struct *mm; loff_t vaddr; - int ret = 0; + int ret; mapping = uprobe->inode->i_mapping; INIT_LIST_HEAD(&try_list); - while ((vi = find_next_vma_info(&try_list, uprobe->offset, - mapping, is_register)) != NULL) { + + ret = 0; + + for (;;) { + vi = find_next_vma_info(&try_list, uprobe->offset, mapping, is_register); + if (!vi) + break; + if (IS_ERR(vi)) { ret = PTR_ERR(vi); break; } + mm = vi->mm; down_read(&mm->mmap_sem); vma = find_vma(mm, (unsigned long)vi->vaddr); @@ -755,19 +782,21 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register) break; } } + list_for_each_entry_safe(vi, tmpvi, &try_list, probe_list) { list_del(&vi->probe_list); kfree(vi); } + return ret; } -static int __register_uprobe(struct uprobe *uprobe) +static int __uprobe_register(struct uprobe *uprobe) { return register_for_each_vma(uprobe, true); } -static void __unregister_uprobe(struct uprobe *uprobe) +static void __uprobe_unregister(struct uprobe *uprobe) { if (!register_for_each_vma(uprobe, false)) delete_uprobe(uprobe); @@ -776,15 +805,15 @@ static void __unregister_uprobe(struct uprobe *uprobe) } /* - * register_uprobe - register a probe + * uprobe_register - register a probe * @inode: the file in which the probe has to be placed. * @offset: offset from the start of the file. * @consumer: information on howto handle the probe.. * - * Apart from the access refcount, register_uprobe() takes a creation + * Apart from the access refcount, uprobe_register() takes a creation * refcount (thro alloc_uprobe) if and only if this @uprobe is getting * inserted into the rbtree (i.e first consumer for a @inode:@offset - * tuple). Creation refcount stops unregister_uprobe from freeing the + * tuple). Creation refcount stops uprobe_unregister from freeing the * @uprobe even before the register operation is complete. Creation * refcount is released when the last @consumer for the @uprobe * unregisters. @@ -792,28 +821,29 @@ static void __unregister_uprobe(struct uprobe *uprobe) * Return errno if it cannot successully install probes * else return 0 (success) */ -int register_uprobe(struct inode *inode, loff_t offset, - struct uprobe_consumer *consumer) +int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) { struct uprobe *uprobe; - int ret = -EINVAL; + int ret; if (!inode || !consumer || consumer->next) - return ret; + return -EINVAL; if (offset > i_size_read(inode)) - return ret; + return -EINVAL; ret = 0; mutex_lock(uprobes_hash(inode)); uprobe = alloc_uprobe(inode, offset); - if (uprobe && !add_consumer(uprobe, consumer)) { - ret = __register_uprobe(uprobe); + + if (uprobe && !consumer_add(uprobe, consumer)) { + ret = __uprobe_register(uprobe); if (ret) { uprobe->consumers = NULL; - __unregister_uprobe(uprobe); - } else + __uprobe_unregister(uprobe); + } else { uprobe->flags |= UPROBES_RUN_HANDLER; + } } mutex_unlock(uprobes_hash(inode)); @@ -823,15 +853,14 @@ int register_uprobe(struct inode *inode, loff_t offset, } /* - * unregister_uprobe - unregister a already registered probe. + * uprobe_unregister - unregister a already registered probe. * @inode: the file in which the probe has to be removed. * @offset: offset from the start of the file. * @consumer: identify which probe if multiple probes are colocated. */ -void unregister_uprobe(struct inode *inode, loff_t offset, - struct uprobe_consumer *consumer) +void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) { - struct uprobe *uprobe = NULL; + struct uprobe *uprobe; if (!inode || !consumer) return; @@ -841,15 +870,14 @@ void unregister_uprobe(struct inode *inode, loff_t offset, return; mutex_lock(uprobes_hash(inode)); - if (!del_consumer(uprobe, consumer)) - goto unreg_out; - if (!uprobe->consumers) { - __unregister_uprobe(uprobe); - uprobe->flags &= ~UPROBES_RUN_HANDLER; + if (consumer_del(uprobe, consumer)) { + if (!uprobe->consumers) { + __uprobe_unregister(uprobe); + uprobe->flags &= ~UPROBES_RUN_HANDLER; + } } -unreg_out: mutex_unlock(uprobes_hash(inode)); if (uprobe) put_uprobe(uprobe); @@ -870,6 +898,7 @@ static struct rb_node *find_least_offset_node(struct inode *inode) while (n) { uprobe = rb_entry(n, struct uprobe, rb_node); match = match_uprobe(&u, uprobe); + if (uprobe->inode == inode) close_node = n; @@ -881,6 +910,7 @@ static struct rb_node *find_least_offset_node(struct inode *inode) else n = n->rb_right; } + return close_node; } @@ -890,11 +920,13 @@ static struct rb_node *find_least_offset_node(struct inode *inode) static void build_probe_list(struct inode *inode, struct list_head *head) { struct uprobe *uprobe; - struct rb_node *n; unsigned long flags; + struct rb_node *n; spin_lock_irqsave(&uprobes_treelock, flags); + n = find_least_offset_node(inode); + for (; n; n = rb_next(n)) { uprobe = rb_entry(n, struct uprobe, rb_node); if (uprobe->inode != inode) @@ -903,6 +935,7 @@ static void build_probe_list(struct inode *inode, struct list_head *head) list_add(&uprobe->pending_list, head); atomic_inc(&uprobe->ref); } + spin_unlock_irqrestore(&uprobes_treelock, flags); } @@ -912,42 +945,44 @@ static void build_probe_list(struct inode *inode, struct list_head *head) * * Return -ve no if we fail to insert probes and we cannot * bail-out. - * Return 0 otherwise. i.e : + * Return 0 otherwise. i.e: + * * - successful insertion of probes * - (or) no possible probes to be inserted. * - (or) insertion of probes failed but we can bail-out. */ -int mmap_uprobe(struct vm_area_struct *vma) +int uprobe_mmap(struct vm_area_struct *vma) { struct list_head tmp_list; struct uprobe *uprobe, *u; struct inode *inode; - int ret = 0; + int ret; if (!atomic_read(&uprobe_events) || !valid_vma(vma, true)) - return ret; /* Bail-out */ + return 0; inode = vma->vm_file->f_mapping->host; if (!inode) - return ret; + return 0; INIT_LIST_HEAD(&tmp_list); mutex_lock(uprobes_mmap_hash(inode)); build_probe_list(inode, &tmp_list); + + ret = 0; + list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { loff_t vaddr; list_del(&uprobe->pending_list); if (!ret) { vaddr = vma_address(vma, uprobe->offset); - if (vaddr < vma->vm_start || vaddr >= vma->vm_end) { - put_uprobe(uprobe); - continue; + if (vaddr >= vma->vm_start && vaddr < vma->vm_end) { + ret = install_breakpoint(vma->vm_mm, uprobe, vma, vaddr); + /* Ignore double add: */ + if (ret == -EEXIST) + ret = 0; } - ret = install_breakpoint(vma->vm_mm, uprobe, vma, - vaddr); - if (ret == -EEXIST) - ret = 0; } put_uprobe(uprobe); } diff --git a/mm/mmap.c b/mm/mmap.c index 1aed183636d7..5a863d328a44 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -618,10 +618,10 @@ again: remove_next = 1 + (end > next->vm_end); mutex_unlock(&mapping->i_mmap_mutex); if (root) { - mmap_uprobe(vma); + uprobe_mmap(vma); if (adjust_next) - mmap_uprobe(next); + uprobe_mmap(next); } if (remove_next) { @@ -646,7 +646,7 @@ again: remove_next = 1 + (end > next->vm_end); } } if (insert && file) - mmap_uprobe(insert); + uprobe_mmap(insert); validate_mm(mm); @@ -1340,7 +1340,7 @@ out: } else if ((flags & MAP_POPULATE) && !(flags & MAP_NONBLOCK)) make_pages_present(addr, addr + len); - if (file && mmap_uprobe(vma)) + if (file && uprobe_mmap(vma)) /* matching probes but cannot insert */ goto unmap_and_free_vma; @@ -2301,7 +2301,7 @@ int insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma) security_vm_enough_memory_mm(mm, vma_pages(vma))) return -ENOMEM; - if (vma->vm_file && mmap_uprobe(vma)) + if (vma->vm_file && uprobe_mmap(vma)) return -EINVAL; vma_link(mm, vma, prev, rb_link, rb_parent); @@ -2374,7 +2374,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap, if (new_vma->vm_file) { get_file(new_vma->vm_file); - if (mmap_uprobe(new_vma)) + if (uprobe_mmap(new_vma)) goto out_free_mempol; if (vma->vm_flags & VM_EXECUTABLE) -- cgit v1.2.3 From a5f4374a9610fd7286c2164d4e680436727eff71 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Wed, 22 Feb 2012 11:01:49 +0100 Subject: uprobes: Move to kernel/events/ Consolidate the uprobes code under kernel/events/, where the various core kernel event handling routines live. Acked-by: Peter Zijlstra Cc: Srikar Dronamraju Cc: Jim Keniston Cc: Oleg Nesterov Cc: Masami Hiramatsu Cc: Arnaldo Carvalho de Melo Cc: Anton Arapov Cc: Ananth N Mavinakayanahalli Link: http://lkml.kernel.org/n/tip-biuyhhwohxgbp2vzbap5yr8o@git.kernel.org Signed-off-by: Ingo Molnar --- arch/Kconfig | 2 +- kernel/Makefile | 1 - kernel/events/Makefile | 3 + kernel/events/uprobes.c | 1011 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/uprobes.c | 1011 ----------------------------------------------- 5 files changed, 1015 insertions(+), 1013 deletions(-) create mode 100644 kernel/events/uprobes.c delete mode 100644 kernel/uprobes.c (limited to 'arch') diff --git a/arch/Kconfig b/arch/Kconfig index cca5b545d806..d0e37c9d5f6b 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -67,7 +67,7 @@ config OPTPROBES config UPROBES bool "Transparent user-space probes (EXPERIMENTAL)" - depends on ARCH_SUPPORTS_UPROBES + depends on ARCH_SUPPORTS_UPROBES && PERF_EVENTS default n help Uprobes is the user-space counterpart to kprobes: they diff --git a/kernel/Makefile b/kernel/Makefile index 8609dd3d875a..2d9de86b7e76 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -107,7 +107,6 @@ obj-$(CONFIG_USER_RETURN_NOTIFIER) += user-return-notifier.o obj-$(CONFIG_PADATA) += padata.o obj-$(CONFIG_CRASH_DUMP) += crash_dump.o obj-$(CONFIG_JUMP_LABEL) += jump_label.o -obj-$(CONFIG_UPROBES) += uprobes.o $(obj)/configs.o: $(obj)/config_data.h diff --git a/kernel/events/Makefile b/kernel/events/Makefile index 22d901f9caf4..103f5d147b2f 100644 --- a/kernel/events/Makefile +++ b/kernel/events/Makefile @@ -3,4 +3,7 @@ CFLAGS_REMOVE_core.o = -pg endif obj-y := core.o ring_buffer.o callchain.o + obj-$(CONFIG_HAVE_HW_BREAKPOINT) += hw_breakpoint.o +obj-$(CONFIG_UPROBES) += uprobes.o + diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c new file mode 100644 index 000000000000..884817f1b0d3 --- /dev/null +++ b/kernel/events/uprobes.c @@ -0,0 +1,1011 @@ +/* + * User-space Probes (UProbes) + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + * + * Copyright (C) IBM Corporation, 2008-2011 + * Authors: + * Srikar Dronamraju + * Jim Keniston + */ + +#include +#include +#include /* read_mapping_page */ +#include +#include +#include /* anon_vma_prepare */ +#include /* set_pte_at_notify */ +#include /* try_to_free_swap */ + +#include + +static struct rb_root uprobes_tree = RB_ROOT; + +static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */ + +#define UPROBES_HASH_SZ 13 + +/* serialize (un)register */ +static struct mutex uprobes_mutex[UPROBES_HASH_SZ]; + +#define uprobes_hash(v) (&uprobes_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ]) + +/* serialize uprobe->pending_list */ +static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ]; +#define uprobes_mmap_hash(v) (&uprobes_mmap_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ]) + +/* + * uprobe_events allows us to skip the uprobe_mmap if there are no uprobe + * events active at this time. Probably a fine grained per inode count is + * better? + */ +static atomic_t uprobe_events = ATOMIC_INIT(0); + +/* + * Maintain a temporary per vma info that can be used to search if a vma + * has already been handled. This structure is introduced since extending + * vm_area_struct wasnt recommended. + */ +struct vma_info { + struct list_head probe_list; + struct mm_struct *mm; + loff_t vaddr; +}; + +/* + * valid_vma: Verify if the specified vma is an executable vma + * Relax restrictions while unregistering: vm_flags might have + * changed after breakpoint was inserted. + * - is_register: indicates if we are in register context. + * - Return 1 if the specified virtual address is in an + * executable vma. + */ +static bool valid_vma(struct vm_area_struct *vma, bool is_register) +{ + if (!vma->vm_file) + return false; + + if (!is_register) + return true; + + if ((vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)) == (VM_READ|VM_EXEC)) + return true; + + return false; +} + +static loff_t vma_address(struct vm_area_struct *vma, loff_t offset) +{ + loff_t vaddr; + + vaddr = vma->vm_start + offset; + vaddr -= vma->vm_pgoff << PAGE_SHIFT; + + return vaddr; +} + +/** + * __replace_page - replace page in vma by new page. + * based on replace_page in mm/ksm.c + * + * @vma: vma that holds the pte pointing to page + * @page: the cowed page we are replacing by kpage + * @kpage: the modified page we replace page by + * + * Returns 0 on success, -EFAULT on failure. + */ +static int __replace_page(struct vm_area_struct *vma, struct page *page, struct page *kpage) +{ + struct mm_struct *mm = vma->vm_mm; + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *ptep; + spinlock_t *ptl; + unsigned long addr; + int err = -EFAULT; + + addr = page_address_in_vma(page, vma); + if (addr == -EFAULT) + goto out; + + pgd = pgd_offset(mm, addr); + if (!pgd_present(*pgd)) + goto out; + + pud = pud_offset(pgd, addr); + if (!pud_present(*pud)) + goto out; + + pmd = pmd_offset(pud, addr); + if (!pmd_present(*pmd)) + goto out; + + ptep = pte_offset_map_lock(mm, pmd, addr, &ptl); + if (!ptep) + goto out; + + get_page(kpage); + page_add_new_anon_rmap(kpage, vma, addr); + + flush_cache_page(vma, addr, pte_pfn(*ptep)); + ptep_clear_flush(vma, addr, ptep); + set_pte_at_notify(mm, addr, ptep, mk_pte(kpage, vma->vm_page_prot)); + + page_remove_rmap(page); + if (!page_mapped(page)) + try_to_free_swap(page); + put_page(page); + pte_unmap_unlock(ptep, ptl); + err = 0; + +out: + return err; +} + +/** + * is_bkpt_insn - check if instruction is breakpoint instruction. + * @insn: instruction to be checked. + * Default implementation of is_bkpt_insn + * Returns true if @insn is a breakpoint instruction. + */ +bool __weak is_bkpt_insn(uprobe_opcode_t *insn) +{ + return *insn == UPROBES_BKPT_INSN; +} + +/* + * NOTE: + * Expect the breakpoint instruction to be the smallest size instruction for + * the architecture. If an arch has variable length instruction and the + * breakpoint instruction is not of the smallest length instruction + * supported by that architecture then we need to modify read_opcode / + * write_opcode accordingly. This would never be a problem for archs that + * have fixed length instructions. + */ + +/* + * write_opcode - write the opcode at a given virtual address. + * @mm: the probed process address space. + * @uprobe: the breakpointing information. + * @vaddr: the virtual address to store the opcode. + * @opcode: opcode to be written at @vaddr. + * + * Called with mm->mmap_sem held (for read and with a reference to + * mm). + * + * For mm @mm, write the opcode at @vaddr. + * Return 0 (success) or a negative errno. + */ +static int write_opcode(struct mm_struct *mm, struct uprobe *uprobe, + unsigned long vaddr, uprobe_opcode_t opcode) +{ + struct page *old_page, *new_page; + struct address_space *mapping; + void *vaddr_old, *vaddr_new; + struct vm_area_struct *vma; + loff_t addr; + int ret; + + /* Read the page with vaddr into memory */ + ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &old_page, &vma); + if (ret <= 0) + return ret; + + ret = -EINVAL; + + /* + * We are interested in text pages only. Our pages of interest + * should be mapped for read and execute only. We desist from + * adding probes in write mapped pages since the breakpoints + * might end up in the file copy. + */ + if (!valid_vma(vma, is_bkpt_insn(&opcode))) + goto put_out; + + mapping = uprobe->inode->i_mapping; + if (mapping != vma->vm_file->f_mapping) + goto put_out; + + addr = vma_address(vma, uprobe->offset); + if (vaddr != (unsigned long)addr) + goto put_out; + + ret = -ENOMEM; + new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr); + if (!new_page) + goto put_out; + + __SetPageUptodate(new_page); + + /* + * lock page will serialize against do_wp_page()'s + * PageAnon() handling + */ + lock_page(old_page); + /* copy the page now that we've got it stable */ + vaddr_old = kmap_atomic(old_page); + vaddr_new = kmap_atomic(new_page); + + memcpy(vaddr_new, vaddr_old, PAGE_SIZE); + + /* poke the new insn in, ASSUMES we don't cross page boundary */ + vaddr &= ~PAGE_MASK; + BUG_ON(vaddr + uprobe_opcode_sz > PAGE_SIZE); + memcpy(vaddr_new + vaddr, &opcode, uprobe_opcode_sz); + + kunmap_atomic(vaddr_new); + kunmap_atomic(vaddr_old); + + ret = anon_vma_prepare(vma); + if (ret) + goto unlock_out; + + lock_page(new_page); + ret = __replace_page(vma, old_page, new_page); + unlock_page(new_page); + +unlock_out: + unlock_page(old_page); + page_cache_release(new_page); + +put_out: + put_page(old_page); + + return ret; +} + +/** + * read_opcode - read the opcode at a given virtual address. + * @mm: the probed process address space. + * @vaddr: the virtual address to read the opcode. + * @opcode: location to store the read opcode. + * + * Called with mm->mmap_sem held (for read and with a reference to + * mm. + * + * For mm @mm, read the opcode at @vaddr and store it in @opcode. + * Return 0 (success) or a negative errno. + */ +static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t *opcode) +{ + struct page *page; + void *vaddr_new; + int ret; + + ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &page, NULL); + if (ret <= 0) + return ret; + + lock_page(page); + vaddr_new = kmap_atomic(page); + vaddr &= ~PAGE_MASK; + memcpy(opcode, vaddr_new + vaddr, uprobe_opcode_sz); + kunmap_atomic(vaddr_new); + unlock_page(page); + + put_page(page); + + return 0; +} + +static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) +{ + uprobe_opcode_t opcode; + int result; + + result = read_opcode(mm, vaddr, &opcode); + if (result) + return result; + + if (is_bkpt_insn(&opcode)) + return 1; + + return 0; +} + +/** + * set_bkpt - store breakpoint at a given address. + * @mm: the probed process address space. + * @uprobe: the probepoint information. + * @vaddr: the virtual address to insert the opcode. + * + * For mm @mm, store the breakpoint instruction at @vaddr. + * Return 0 (success) or a negative errno. + */ +int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr) +{ + int result; + + result = is_bkpt_at_addr(mm, vaddr); + if (result == 1) + return -EEXIST; + + if (result) + return result; + + return write_opcode(mm, uprobe, vaddr, UPROBES_BKPT_INSN); +} + +/** + * set_orig_insn - Restore the original instruction. + * @mm: the probed process address space. + * @uprobe: the probepoint information. + * @vaddr: the virtual address to insert the opcode. + * @verify: if true, verify existance of breakpoint instruction. + * + * For mm @mm, restore the original opcode (opcode) at @vaddr. + * Return 0 (success) or a negative errno. + */ +int __weak +set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr, bool verify) +{ + if (verify) { + int result; + + result = is_bkpt_at_addr(mm, vaddr); + if (!result) + return -EINVAL; + + if (result != 1) + return result; + } + return write_opcode(mm, uprobe, vaddr, *(uprobe_opcode_t *)uprobe->insn); +} + +static int match_uprobe(struct uprobe *l, struct uprobe *r) +{ + if (l->inode < r->inode) + return -1; + + if (l->inode > r->inode) + return 1; + + if (l->offset < r->offset) + return -1; + + if (l->offset > r->offset) + return 1; + + return 0; +} + +static struct uprobe *__find_uprobe(struct inode *inode, loff_t offset) +{ + struct uprobe u = { .inode = inode, .offset = offset }; + struct rb_node *n = uprobes_tree.rb_node; + struct uprobe *uprobe; + int match; + + while (n) { + uprobe = rb_entry(n, struct uprobe, rb_node); + match = match_uprobe(&u, uprobe); + if (!match) { + atomic_inc(&uprobe->ref); + return uprobe; + } + + if (match < 0) + n = n->rb_left; + else + n = n->rb_right; + } + return NULL; +} + +/* + * Find a uprobe corresponding to a given inode:offset + * Acquires uprobes_treelock + */ +static struct uprobe *find_uprobe(struct inode *inode, loff_t offset) +{ + struct uprobe *uprobe; + unsigned long flags; + + spin_lock_irqsave(&uprobes_treelock, flags); + uprobe = __find_uprobe(inode, offset); + spin_unlock_irqrestore(&uprobes_treelock, flags); + + return uprobe; +} + +static struct uprobe *__insert_uprobe(struct uprobe *uprobe) +{ + struct rb_node **p = &uprobes_tree.rb_node; + struct rb_node *parent = NULL; + struct uprobe *u; + int match; + + while (*p) { + parent = *p; + u = rb_entry(parent, struct uprobe, rb_node); + match = match_uprobe(uprobe, u); + if (!match) { + atomic_inc(&u->ref); + return u; + } + + if (match < 0) + p = &parent->rb_left; + else + p = &parent->rb_right; + + } + + u = NULL; + rb_link_node(&uprobe->rb_node, parent, p); + rb_insert_color(&uprobe->rb_node, &uprobes_tree); + /* get access + creation ref */ + atomic_set(&uprobe->ref, 2); + + return u; +} + +/* + * Acquire uprobes_treelock. + * Matching uprobe already exists in rbtree; + * increment (access refcount) and return the matching uprobe. + * + * No matching uprobe; insert the uprobe in rb_tree; + * get a double refcount (access + creation) and return NULL. + */ +static struct uprobe *insert_uprobe(struct uprobe *uprobe) +{ + unsigned long flags; + struct uprobe *u; + + spin_lock_irqsave(&uprobes_treelock, flags); + u = __insert_uprobe(uprobe); + spin_unlock_irqrestore(&uprobes_treelock, flags); + + return u; +} + +static void put_uprobe(struct uprobe *uprobe) +{ + if (atomic_dec_and_test(&uprobe->ref)) + kfree(uprobe); +} + +static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) +{ + struct uprobe *uprobe, *cur_uprobe; + + uprobe = kzalloc(sizeof(struct uprobe), GFP_KERNEL); + if (!uprobe) + return NULL; + + uprobe->inode = igrab(inode); + uprobe->offset = offset; + init_rwsem(&uprobe->consumer_rwsem); + INIT_LIST_HEAD(&uprobe->pending_list); + + /* add to uprobes_tree, sorted on inode:offset */ + cur_uprobe = insert_uprobe(uprobe); + + /* a uprobe exists for this inode:offset combination */ + if (cur_uprobe) { + kfree(uprobe); + uprobe = cur_uprobe; + iput(inode); + } else { + atomic_inc(&uprobe_events); + } + + return uprobe; +} + +/* Returns the previous consumer */ +static struct uprobe_consumer * +consumer_add(struct uprobe *uprobe, struct uprobe_consumer *consumer) +{ + down_write(&uprobe->consumer_rwsem); + consumer->next = uprobe->consumers; + uprobe->consumers = consumer; + up_write(&uprobe->consumer_rwsem); + + return consumer->next; +} + +/* + * For uprobe @uprobe, delete the consumer @consumer. + * Return true if the @consumer is deleted successfully + * or return false. + */ +static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *consumer) +{ + struct uprobe_consumer **con; + bool ret = false; + + down_write(&uprobe->consumer_rwsem); + for (con = &uprobe->consumers; *con; con = &(*con)->next) { + if (*con == consumer) { + *con = consumer->next; + ret = true; + break; + } + } + up_write(&uprobe->consumer_rwsem); + + return ret; +} + +static int __copy_insn(struct address_space *mapping, + struct vm_area_struct *vma, char *insn, + unsigned long nbytes, unsigned long offset) +{ + struct file *filp = vma->vm_file; + struct page *page; + void *vaddr; + unsigned long off1; + unsigned long idx; + + if (!filp) + return -EINVAL; + + idx = (unsigned long)(offset >> PAGE_CACHE_SHIFT); + off1 = offset &= ~PAGE_MASK; + + /* + * Ensure that the page that has the original instruction is + * populated and in page-cache. + */ + page = read_mapping_page(mapping, idx, filp); + if (IS_ERR(page)) + return PTR_ERR(page); + + vaddr = kmap_atomic(page); + memcpy(insn, vaddr + off1, nbytes); + kunmap_atomic(vaddr); + page_cache_release(page); + + return 0; +} + +static int copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr) +{ + struct address_space *mapping; + unsigned long nbytes; + int bytes; + + addr &= ~PAGE_MASK; + nbytes = PAGE_SIZE - addr; + mapping = uprobe->inode->i_mapping; + + /* Instruction at end of binary; copy only available bytes */ + if (uprobe->offset + MAX_UINSN_BYTES > uprobe->inode->i_size) + bytes = uprobe->inode->i_size - uprobe->offset; + else + bytes = MAX_UINSN_BYTES; + + /* Instruction at the page-boundary; copy bytes in second page */ + if (nbytes < bytes) { + if (__copy_insn(mapping, vma, uprobe->insn + nbytes, + bytes - nbytes, uprobe->offset + nbytes)) + return -ENOMEM; + + bytes = nbytes; + } + return __copy_insn(mapping, vma, uprobe->insn, bytes, uprobe->offset); +} + +static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, + struct vm_area_struct *vma, loff_t vaddr) +{ + unsigned long addr; + int ret; + + /* + * If probe is being deleted, unregister thread could be done with + * the vma-rmap-walk through. Adding a probe now can be fatal since + * nobody will be able to cleanup. Also we could be from fork or + * mremap path, where the probe might have already been inserted. + * Hence behave as if probe already existed. + */ + if (!uprobe->consumers) + return -EEXIST; + + addr = (unsigned long)vaddr; + + if (!(uprobe->flags & UPROBES_COPY_INSN)) { + ret = copy_insn(uprobe, vma, addr); + if (ret) + return ret; + + if (is_bkpt_insn((uprobe_opcode_t *)uprobe->insn)) + return -EEXIST; + + ret = arch_uprobes_analyze_insn(mm, uprobe); + if (ret) + return ret; + + uprobe->flags |= UPROBES_COPY_INSN; + } + ret = set_bkpt(mm, uprobe, addr); + + return ret; +} + +static void remove_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, loff_t vaddr) +{ + set_orig_insn(mm, uprobe, (unsigned long)vaddr, true); +} + +static void delete_uprobe(struct uprobe *uprobe) +{ + unsigned long flags; + + spin_lock_irqsave(&uprobes_treelock, flags); + rb_erase(&uprobe->rb_node, &uprobes_tree); + spin_unlock_irqrestore(&uprobes_treelock, flags); + iput(uprobe->inode); + put_uprobe(uprobe); + atomic_dec(&uprobe_events); +} + +static struct vma_info *__find_next_vma_info(struct list_head *head, + loff_t offset, struct address_space *mapping, + struct vma_info *vi, bool is_register) +{ + struct prio_tree_iter iter; + struct vm_area_struct *vma; + struct vma_info *tmpvi; + unsigned long pgoff; + int existing_vma; + loff_t vaddr; + + pgoff = offset >> PAGE_SHIFT; + + vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { + if (!valid_vma(vma, is_register)) + continue; + + existing_vma = 0; + vaddr = vma_address(vma, offset); + + list_for_each_entry(tmpvi, head, probe_list) { + if (tmpvi->mm == vma->vm_mm && tmpvi->vaddr == vaddr) { + existing_vma = 1; + break; + } + } + + /* + * Another vma needs a probe to be installed. However skip + * installing the probe if the vma is about to be unlinked. + */ + if (!existing_vma && atomic_inc_not_zero(&vma->vm_mm->mm_users)) { + vi->mm = vma->vm_mm; + vi->vaddr = vaddr; + list_add(&vi->probe_list, head); + + return vi; + } + } + + return NULL; +} + +/* + * Iterate in the rmap prio tree and find a vma where a probe has not + * yet been inserted. + */ +static struct vma_info * +find_next_vma_info(struct list_head *head, loff_t offset, struct address_space *mapping, + bool is_register) +{ + struct vma_info *vi, *retvi; + + vi = kzalloc(sizeof(struct vma_info), GFP_KERNEL); + if (!vi) + return ERR_PTR(-ENOMEM); + + mutex_lock(&mapping->i_mmap_mutex); + retvi = __find_next_vma_info(head, offset, mapping, vi, is_register); + mutex_unlock(&mapping->i_mmap_mutex); + + if (!retvi) + kfree(vi); + + return retvi; +} + +static int register_for_each_vma(struct uprobe *uprobe, bool is_register) +{ + struct list_head try_list; + struct vm_area_struct *vma; + struct address_space *mapping; + struct vma_info *vi, *tmpvi; + struct mm_struct *mm; + loff_t vaddr; + int ret; + + mapping = uprobe->inode->i_mapping; + INIT_LIST_HEAD(&try_list); + + ret = 0; + + for (;;) { + vi = find_next_vma_info(&try_list, uprobe->offset, mapping, is_register); + if (!vi) + break; + + if (IS_ERR(vi)) { + ret = PTR_ERR(vi); + break; + } + + mm = vi->mm; + down_read(&mm->mmap_sem); + vma = find_vma(mm, (unsigned long)vi->vaddr); + if (!vma || !valid_vma(vma, is_register)) { + list_del(&vi->probe_list); + kfree(vi); + up_read(&mm->mmap_sem); + mmput(mm); + continue; + } + vaddr = vma_address(vma, uprobe->offset); + if (vma->vm_file->f_mapping->host != uprobe->inode || + vaddr != vi->vaddr) { + list_del(&vi->probe_list); + kfree(vi); + up_read(&mm->mmap_sem); + mmput(mm); + continue; + } + + if (is_register) + ret = install_breakpoint(mm, uprobe, vma, vi->vaddr); + else + remove_breakpoint(mm, uprobe, vi->vaddr); + + up_read(&mm->mmap_sem); + mmput(mm); + if (is_register) { + if (ret && ret == -EEXIST) + ret = 0; + if (ret) + break; + } + } + + list_for_each_entry_safe(vi, tmpvi, &try_list, probe_list) { + list_del(&vi->probe_list); + kfree(vi); + } + + return ret; +} + +static int __uprobe_register(struct uprobe *uprobe) +{ + return register_for_each_vma(uprobe, true); +} + +static void __uprobe_unregister(struct uprobe *uprobe) +{ + if (!register_for_each_vma(uprobe, false)) + delete_uprobe(uprobe); + + /* TODO : cant unregister? schedule a worker thread */ +} + +/* + * uprobe_register - register a probe + * @inode: the file in which the probe has to be placed. + * @offset: offset from the start of the file. + * @consumer: information on howto handle the probe.. + * + * Apart from the access refcount, uprobe_register() takes a creation + * refcount (thro alloc_uprobe) if and only if this @uprobe is getting + * inserted into the rbtree (i.e first consumer for a @inode:@offset + * tuple). Creation refcount stops uprobe_unregister from freeing the + * @uprobe even before the register operation is complete. Creation + * refcount is released when the last @consumer for the @uprobe + * unregisters. + * + * Return errno if it cannot successully install probes + * else return 0 (success) + */ +int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) +{ + struct uprobe *uprobe; + int ret; + + if (!inode || !consumer || consumer->next) + return -EINVAL; + + if (offset > i_size_read(inode)) + return -EINVAL; + + ret = 0; + mutex_lock(uprobes_hash(inode)); + uprobe = alloc_uprobe(inode, offset); + + if (uprobe && !consumer_add(uprobe, consumer)) { + ret = __uprobe_register(uprobe); + if (ret) { + uprobe->consumers = NULL; + __uprobe_unregister(uprobe); + } else { + uprobe->flags |= UPROBES_RUN_HANDLER; + } + } + + mutex_unlock(uprobes_hash(inode)); + put_uprobe(uprobe); + + return ret; +} + +/* + * uprobe_unregister - unregister a already registered probe. + * @inode: the file in which the probe has to be removed. + * @offset: offset from the start of the file. + * @consumer: identify which probe if multiple probes are colocated. + */ +void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) +{ + struct uprobe *uprobe; + + if (!inode || !consumer) + return; + + uprobe = find_uprobe(inode, offset); + if (!uprobe) + return; + + mutex_lock(uprobes_hash(inode)); + + if (consumer_del(uprobe, consumer)) { + if (!uprobe->consumers) { + __uprobe_unregister(uprobe); + uprobe->flags &= ~UPROBES_RUN_HANDLER; + } + } + + mutex_unlock(uprobes_hash(inode)); + if (uprobe) + put_uprobe(uprobe); +} + +/* + * Of all the nodes that correspond to the given inode, return the node + * with the least offset. + */ +static struct rb_node *find_least_offset_node(struct inode *inode) +{ + struct uprobe u = { .inode = inode, .offset = 0}; + struct rb_node *n = uprobes_tree.rb_node; + struct rb_node *close_node = NULL; + struct uprobe *uprobe; + int match; + + while (n) { + uprobe = rb_entry(n, struct uprobe, rb_node); + match = match_uprobe(&u, uprobe); + + if (uprobe->inode == inode) + close_node = n; + + if (!match) + return close_node; + + if (match < 0) + n = n->rb_left; + else + n = n->rb_right; + } + + return close_node; +} + +/* + * For a given inode, build a list of probes that need to be inserted. + */ +static void build_probe_list(struct inode *inode, struct list_head *head) +{ + struct uprobe *uprobe; + unsigned long flags; + struct rb_node *n; + + spin_lock_irqsave(&uprobes_treelock, flags); + + n = find_least_offset_node(inode); + + for (; n; n = rb_next(n)) { + uprobe = rb_entry(n, struct uprobe, rb_node); + if (uprobe->inode != inode) + break; + + list_add(&uprobe->pending_list, head); + atomic_inc(&uprobe->ref); + } + + spin_unlock_irqrestore(&uprobes_treelock, flags); +} + +/* + * Called from mmap_region. + * called with mm->mmap_sem acquired. + * + * Return -ve no if we fail to insert probes and we cannot + * bail-out. + * Return 0 otherwise. i.e: + * + * - successful insertion of probes + * - (or) no possible probes to be inserted. + * - (or) insertion of probes failed but we can bail-out. + */ +int uprobe_mmap(struct vm_area_struct *vma) +{ + struct list_head tmp_list; + struct uprobe *uprobe, *u; + struct inode *inode; + int ret; + + if (!atomic_read(&uprobe_events) || !valid_vma(vma, true)) + return 0; + + inode = vma->vm_file->f_mapping->host; + if (!inode) + return 0; + + INIT_LIST_HEAD(&tmp_list); + mutex_lock(uprobes_mmap_hash(inode)); + build_probe_list(inode, &tmp_list); + + ret = 0; + + list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { + loff_t vaddr; + + list_del(&uprobe->pending_list); + if (!ret) { + vaddr = vma_address(vma, uprobe->offset); + if (vaddr >= vma->vm_start && vaddr < vma->vm_end) { + ret = install_breakpoint(vma->vm_mm, uprobe, vma, vaddr); + /* Ignore double add: */ + if (ret == -EEXIST) + ret = 0; + } + } + put_uprobe(uprobe); + } + + mutex_unlock(uprobes_mmap_hash(inode)); + + return ret; +} + +static int __init init_uprobes(void) +{ + int i; + + for (i = 0; i < UPROBES_HASH_SZ; i++) { + mutex_init(&uprobes_mutex[i]); + mutex_init(&uprobes_mmap_mutex[i]); + } + return 0; +} + +static void __exit exit_uprobes(void) +{ +} + +module_init(init_uprobes); +module_exit(exit_uprobes); diff --git a/kernel/uprobes.c b/kernel/uprobes.c deleted file mode 100644 index 884817f1b0d3..000000000000 --- a/kernel/uprobes.c +++ /dev/null @@ -1,1011 +0,0 @@ -/* - * User-space Probes (UProbes) - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * Copyright (C) IBM Corporation, 2008-2011 - * Authors: - * Srikar Dronamraju - * Jim Keniston - */ - -#include -#include -#include /* read_mapping_page */ -#include -#include -#include /* anon_vma_prepare */ -#include /* set_pte_at_notify */ -#include /* try_to_free_swap */ - -#include - -static struct rb_root uprobes_tree = RB_ROOT; - -static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */ - -#define UPROBES_HASH_SZ 13 - -/* serialize (un)register */ -static struct mutex uprobes_mutex[UPROBES_HASH_SZ]; - -#define uprobes_hash(v) (&uprobes_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ]) - -/* serialize uprobe->pending_list */ -static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ]; -#define uprobes_mmap_hash(v) (&uprobes_mmap_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ]) - -/* - * uprobe_events allows us to skip the uprobe_mmap if there are no uprobe - * events active at this time. Probably a fine grained per inode count is - * better? - */ -static atomic_t uprobe_events = ATOMIC_INIT(0); - -/* - * Maintain a temporary per vma info that can be used to search if a vma - * has already been handled. This structure is introduced since extending - * vm_area_struct wasnt recommended. - */ -struct vma_info { - struct list_head probe_list; - struct mm_struct *mm; - loff_t vaddr; -}; - -/* - * valid_vma: Verify if the specified vma is an executable vma - * Relax restrictions while unregistering: vm_flags might have - * changed after breakpoint was inserted. - * - is_register: indicates if we are in register context. - * - Return 1 if the specified virtual address is in an - * executable vma. - */ -static bool valid_vma(struct vm_area_struct *vma, bool is_register) -{ - if (!vma->vm_file) - return false; - - if (!is_register) - return true; - - if ((vma->vm_flags & (VM_READ|VM_WRITE|VM_EXEC|VM_SHARED)) == (VM_READ|VM_EXEC)) - return true; - - return false; -} - -static loff_t vma_address(struct vm_area_struct *vma, loff_t offset) -{ - loff_t vaddr; - - vaddr = vma->vm_start + offset; - vaddr -= vma->vm_pgoff << PAGE_SHIFT; - - return vaddr; -} - -/** - * __replace_page - replace page in vma by new page. - * based on replace_page in mm/ksm.c - * - * @vma: vma that holds the pte pointing to page - * @page: the cowed page we are replacing by kpage - * @kpage: the modified page we replace page by - * - * Returns 0 on success, -EFAULT on failure. - */ -static int __replace_page(struct vm_area_struct *vma, struct page *page, struct page *kpage) -{ - struct mm_struct *mm = vma->vm_mm; - pgd_t *pgd; - pud_t *pud; - pmd_t *pmd; - pte_t *ptep; - spinlock_t *ptl; - unsigned long addr; - int err = -EFAULT; - - addr = page_address_in_vma(page, vma); - if (addr == -EFAULT) - goto out; - - pgd = pgd_offset(mm, addr); - if (!pgd_present(*pgd)) - goto out; - - pud = pud_offset(pgd, addr); - if (!pud_present(*pud)) - goto out; - - pmd = pmd_offset(pud, addr); - if (!pmd_present(*pmd)) - goto out; - - ptep = pte_offset_map_lock(mm, pmd, addr, &ptl); - if (!ptep) - goto out; - - get_page(kpage); - page_add_new_anon_rmap(kpage, vma, addr); - - flush_cache_page(vma, addr, pte_pfn(*ptep)); - ptep_clear_flush(vma, addr, ptep); - set_pte_at_notify(mm, addr, ptep, mk_pte(kpage, vma->vm_page_prot)); - - page_remove_rmap(page); - if (!page_mapped(page)) - try_to_free_swap(page); - put_page(page); - pte_unmap_unlock(ptep, ptl); - err = 0; - -out: - return err; -} - -/** - * is_bkpt_insn - check if instruction is breakpoint instruction. - * @insn: instruction to be checked. - * Default implementation of is_bkpt_insn - * Returns true if @insn is a breakpoint instruction. - */ -bool __weak is_bkpt_insn(uprobe_opcode_t *insn) -{ - return *insn == UPROBES_BKPT_INSN; -} - -/* - * NOTE: - * Expect the breakpoint instruction to be the smallest size instruction for - * the architecture. If an arch has variable length instruction and the - * breakpoint instruction is not of the smallest length instruction - * supported by that architecture then we need to modify read_opcode / - * write_opcode accordingly. This would never be a problem for archs that - * have fixed length instructions. - */ - -/* - * write_opcode - write the opcode at a given virtual address. - * @mm: the probed process address space. - * @uprobe: the breakpointing information. - * @vaddr: the virtual address to store the opcode. - * @opcode: opcode to be written at @vaddr. - * - * Called with mm->mmap_sem held (for read and with a reference to - * mm). - * - * For mm @mm, write the opcode at @vaddr. - * Return 0 (success) or a negative errno. - */ -static int write_opcode(struct mm_struct *mm, struct uprobe *uprobe, - unsigned long vaddr, uprobe_opcode_t opcode) -{ - struct page *old_page, *new_page; - struct address_space *mapping; - void *vaddr_old, *vaddr_new; - struct vm_area_struct *vma; - loff_t addr; - int ret; - - /* Read the page with vaddr into memory */ - ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &old_page, &vma); - if (ret <= 0) - return ret; - - ret = -EINVAL; - - /* - * We are interested in text pages only. Our pages of interest - * should be mapped for read and execute only. We desist from - * adding probes in write mapped pages since the breakpoints - * might end up in the file copy. - */ - if (!valid_vma(vma, is_bkpt_insn(&opcode))) - goto put_out; - - mapping = uprobe->inode->i_mapping; - if (mapping != vma->vm_file->f_mapping) - goto put_out; - - addr = vma_address(vma, uprobe->offset); - if (vaddr != (unsigned long)addr) - goto put_out; - - ret = -ENOMEM; - new_page = alloc_page_vma(GFP_HIGHUSER_MOVABLE, vma, vaddr); - if (!new_page) - goto put_out; - - __SetPageUptodate(new_page); - - /* - * lock page will serialize against do_wp_page()'s - * PageAnon() handling - */ - lock_page(old_page); - /* copy the page now that we've got it stable */ - vaddr_old = kmap_atomic(old_page); - vaddr_new = kmap_atomic(new_page); - - memcpy(vaddr_new, vaddr_old, PAGE_SIZE); - - /* poke the new insn in, ASSUMES we don't cross page boundary */ - vaddr &= ~PAGE_MASK; - BUG_ON(vaddr + uprobe_opcode_sz > PAGE_SIZE); - memcpy(vaddr_new + vaddr, &opcode, uprobe_opcode_sz); - - kunmap_atomic(vaddr_new); - kunmap_atomic(vaddr_old); - - ret = anon_vma_prepare(vma); - if (ret) - goto unlock_out; - - lock_page(new_page); - ret = __replace_page(vma, old_page, new_page); - unlock_page(new_page); - -unlock_out: - unlock_page(old_page); - page_cache_release(new_page); - -put_out: - put_page(old_page); - - return ret; -} - -/** - * read_opcode - read the opcode at a given virtual address. - * @mm: the probed process address space. - * @vaddr: the virtual address to read the opcode. - * @opcode: location to store the read opcode. - * - * Called with mm->mmap_sem held (for read and with a reference to - * mm. - * - * For mm @mm, read the opcode at @vaddr and store it in @opcode. - * Return 0 (success) or a negative errno. - */ -static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t *opcode) -{ - struct page *page; - void *vaddr_new; - int ret; - - ret = get_user_pages(NULL, mm, vaddr, 1, 0, 0, &page, NULL); - if (ret <= 0) - return ret; - - lock_page(page); - vaddr_new = kmap_atomic(page); - vaddr &= ~PAGE_MASK; - memcpy(opcode, vaddr_new + vaddr, uprobe_opcode_sz); - kunmap_atomic(vaddr_new); - unlock_page(page); - - put_page(page); - - return 0; -} - -static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) -{ - uprobe_opcode_t opcode; - int result; - - result = read_opcode(mm, vaddr, &opcode); - if (result) - return result; - - if (is_bkpt_insn(&opcode)) - return 1; - - return 0; -} - -/** - * set_bkpt - store breakpoint at a given address. - * @mm: the probed process address space. - * @uprobe: the probepoint information. - * @vaddr: the virtual address to insert the opcode. - * - * For mm @mm, store the breakpoint instruction at @vaddr. - * Return 0 (success) or a negative errno. - */ -int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr) -{ - int result; - - result = is_bkpt_at_addr(mm, vaddr); - if (result == 1) - return -EEXIST; - - if (result) - return result; - - return write_opcode(mm, uprobe, vaddr, UPROBES_BKPT_INSN); -} - -/** - * set_orig_insn - Restore the original instruction. - * @mm: the probed process address space. - * @uprobe: the probepoint information. - * @vaddr: the virtual address to insert the opcode. - * @verify: if true, verify existance of breakpoint instruction. - * - * For mm @mm, restore the original opcode (opcode) at @vaddr. - * Return 0 (success) or a negative errno. - */ -int __weak -set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr, bool verify) -{ - if (verify) { - int result; - - result = is_bkpt_at_addr(mm, vaddr); - if (!result) - return -EINVAL; - - if (result != 1) - return result; - } - return write_opcode(mm, uprobe, vaddr, *(uprobe_opcode_t *)uprobe->insn); -} - -static int match_uprobe(struct uprobe *l, struct uprobe *r) -{ - if (l->inode < r->inode) - return -1; - - if (l->inode > r->inode) - return 1; - - if (l->offset < r->offset) - return -1; - - if (l->offset > r->offset) - return 1; - - return 0; -} - -static struct uprobe *__find_uprobe(struct inode *inode, loff_t offset) -{ - struct uprobe u = { .inode = inode, .offset = offset }; - struct rb_node *n = uprobes_tree.rb_node; - struct uprobe *uprobe; - int match; - - while (n) { - uprobe = rb_entry(n, struct uprobe, rb_node); - match = match_uprobe(&u, uprobe); - if (!match) { - atomic_inc(&uprobe->ref); - return uprobe; - } - - if (match < 0) - n = n->rb_left; - else - n = n->rb_right; - } - return NULL; -} - -/* - * Find a uprobe corresponding to a given inode:offset - * Acquires uprobes_treelock - */ -static struct uprobe *find_uprobe(struct inode *inode, loff_t offset) -{ - struct uprobe *uprobe; - unsigned long flags; - - spin_lock_irqsave(&uprobes_treelock, flags); - uprobe = __find_uprobe(inode, offset); - spin_unlock_irqrestore(&uprobes_treelock, flags); - - return uprobe; -} - -static struct uprobe *__insert_uprobe(struct uprobe *uprobe) -{ - struct rb_node **p = &uprobes_tree.rb_node; - struct rb_node *parent = NULL; - struct uprobe *u; - int match; - - while (*p) { - parent = *p; - u = rb_entry(parent, struct uprobe, rb_node); - match = match_uprobe(uprobe, u); - if (!match) { - atomic_inc(&u->ref); - return u; - } - - if (match < 0) - p = &parent->rb_left; - else - p = &parent->rb_right; - - } - - u = NULL; - rb_link_node(&uprobe->rb_node, parent, p); - rb_insert_color(&uprobe->rb_node, &uprobes_tree); - /* get access + creation ref */ - atomic_set(&uprobe->ref, 2); - - return u; -} - -/* - * Acquire uprobes_treelock. - * Matching uprobe already exists in rbtree; - * increment (access refcount) and return the matching uprobe. - * - * No matching uprobe; insert the uprobe in rb_tree; - * get a double refcount (access + creation) and return NULL. - */ -static struct uprobe *insert_uprobe(struct uprobe *uprobe) -{ - unsigned long flags; - struct uprobe *u; - - spin_lock_irqsave(&uprobes_treelock, flags); - u = __insert_uprobe(uprobe); - spin_unlock_irqrestore(&uprobes_treelock, flags); - - return u; -} - -static void put_uprobe(struct uprobe *uprobe) -{ - if (atomic_dec_and_test(&uprobe->ref)) - kfree(uprobe); -} - -static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) -{ - struct uprobe *uprobe, *cur_uprobe; - - uprobe = kzalloc(sizeof(struct uprobe), GFP_KERNEL); - if (!uprobe) - return NULL; - - uprobe->inode = igrab(inode); - uprobe->offset = offset; - init_rwsem(&uprobe->consumer_rwsem); - INIT_LIST_HEAD(&uprobe->pending_list); - - /* add to uprobes_tree, sorted on inode:offset */ - cur_uprobe = insert_uprobe(uprobe); - - /* a uprobe exists for this inode:offset combination */ - if (cur_uprobe) { - kfree(uprobe); - uprobe = cur_uprobe; - iput(inode); - } else { - atomic_inc(&uprobe_events); - } - - return uprobe; -} - -/* Returns the previous consumer */ -static struct uprobe_consumer * -consumer_add(struct uprobe *uprobe, struct uprobe_consumer *consumer) -{ - down_write(&uprobe->consumer_rwsem); - consumer->next = uprobe->consumers; - uprobe->consumers = consumer; - up_write(&uprobe->consumer_rwsem); - - return consumer->next; -} - -/* - * For uprobe @uprobe, delete the consumer @consumer. - * Return true if the @consumer is deleted successfully - * or return false. - */ -static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *consumer) -{ - struct uprobe_consumer **con; - bool ret = false; - - down_write(&uprobe->consumer_rwsem); - for (con = &uprobe->consumers; *con; con = &(*con)->next) { - if (*con == consumer) { - *con = consumer->next; - ret = true; - break; - } - } - up_write(&uprobe->consumer_rwsem); - - return ret; -} - -static int __copy_insn(struct address_space *mapping, - struct vm_area_struct *vma, char *insn, - unsigned long nbytes, unsigned long offset) -{ - struct file *filp = vma->vm_file; - struct page *page; - void *vaddr; - unsigned long off1; - unsigned long idx; - - if (!filp) - return -EINVAL; - - idx = (unsigned long)(offset >> PAGE_CACHE_SHIFT); - off1 = offset &= ~PAGE_MASK; - - /* - * Ensure that the page that has the original instruction is - * populated and in page-cache. - */ - page = read_mapping_page(mapping, idx, filp); - if (IS_ERR(page)) - return PTR_ERR(page); - - vaddr = kmap_atomic(page); - memcpy(insn, vaddr + off1, nbytes); - kunmap_atomic(vaddr); - page_cache_release(page); - - return 0; -} - -static int copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr) -{ - struct address_space *mapping; - unsigned long nbytes; - int bytes; - - addr &= ~PAGE_MASK; - nbytes = PAGE_SIZE - addr; - mapping = uprobe->inode->i_mapping; - - /* Instruction at end of binary; copy only available bytes */ - if (uprobe->offset + MAX_UINSN_BYTES > uprobe->inode->i_size) - bytes = uprobe->inode->i_size - uprobe->offset; - else - bytes = MAX_UINSN_BYTES; - - /* Instruction at the page-boundary; copy bytes in second page */ - if (nbytes < bytes) { - if (__copy_insn(mapping, vma, uprobe->insn + nbytes, - bytes - nbytes, uprobe->offset + nbytes)) - return -ENOMEM; - - bytes = nbytes; - } - return __copy_insn(mapping, vma, uprobe->insn, bytes, uprobe->offset); -} - -static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, - struct vm_area_struct *vma, loff_t vaddr) -{ - unsigned long addr; - int ret; - - /* - * If probe is being deleted, unregister thread could be done with - * the vma-rmap-walk through. Adding a probe now can be fatal since - * nobody will be able to cleanup. Also we could be from fork or - * mremap path, where the probe might have already been inserted. - * Hence behave as if probe already existed. - */ - if (!uprobe->consumers) - return -EEXIST; - - addr = (unsigned long)vaddr; - - if (!(uprobe->flags & UPROBES_COPY_INSN)) { - ret = copy_insn(uprobe, vma, addr); - if (ret) - return ret; - - if (is_bkpt_insn((uprobe_opcode_t *)uprobe->insn)) - return -EEXIST; - - ret = arch_uprobes_analyze_insn(mm, uprobe); - if (ret) - return ret; - - uprobe->flags |= UPROBES_COPY_INSN; - } - ret = set_bkpt(mm, uprobe, addr); - - return ret; -} - -static void remove_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, loff_t vaddr) -{ - set_orig_insn(mm, uprobe, (unsigned long)vaddr, true); -} - -static void delete_uprobe(struct uprobe *uprobe) -{ - unsigned long flags; - - spin_lock_irqsave(&uprobes_treelock, flags); - rb_erase(&uprobe->rb_node, &uprobes_tree); - spin_unlock_irqrestore(&uprobes_treelock, flags); - iput(uprobe->inode); - put_uprobe(uprobe); - atomic_dec(&uprobe_events); -} - -static struct vma_info *__find_next_vma_info(struct list_head *head, - loff_t offset, struct address_space *mapping, - struct vma_info *vi, bool is_register) -{ - struct prio_tree_iter iter; - struct vm_area_struct *vma; - struct vma_info *tmpvi; - unsigned long pgoff; - int existing_vma; - loff_t vaddr; - - pgoff = offset >> PAGE_SHIFT; - - vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) { - if (!valid_vma(vma, is_register)) - continue; - - existing_vma = 0; - vaddr = vma_address(vma, offset); - - list_for_each_entry(tmpvi, head, probe_list) { - if (tmpvi->mm == vma->vm_mm && tmpvi->vaddr == vaddr) { - existing_vma = 1; - break; - } - } - - /* - * Another vma needs a probe to be installed. However skip - * installing the probe if the vma is about to be unlinked. - */ - if (!existing_vma && atomic_inc_not_zero(&vma->vm_mm->mm_users)) { - vi->mm = vma->vm_mm; - vi->vaddr = vaddr; - list_add(&vi->probe_list, head); - - return vi; - } - } - - return NULL; -} - -/* - * Iterate in the rmap prio tree and find a vma where a probe has not - * yet been inserted. - */ -static struct vma_info * -find_next_vma_info(struct list_head *head, loff_t offset, struct address_space *mapping, - bool is_register) -{ - struct vma_info *vi, *retvi; - - vi = kzalloc(sizeof(struct vma_info), GFP_KERNEL); - if (!vi) - return ERR_PTR(-ENOMEM); - - mutex_lock(&mapping->i_mmap_mutex); - retvi = __find_next_vma_info(head, offset, mapping, vi, is_register); - mutex_unlock(&mapping->i_mmap_mutex); - - if (!retvi) - kfree(vi); - - return retvi; -} - -static int register_for_each_vma(struct uprobe *uprobe, bool is_register) -{ - struct list_head try_list; - struct vm_area_struct *vma; - struct address_space *mapping; - struct vma_info *vi, *tmpvi; - struct mm_struct *mm; - loff_t vaddr; - int ret; - - mapping = uprobe->inode->i_mapping; - INIT_LIST_HEAD(&try_list); - - ret = 0; - - for (;;) { - vi = find_next_vma_info(&try_list, uprobe->offset, mapping, is_register); - if (!vi) - break; - - if (IS_ERR(vi)) { - ret = PTR_ERR(vi); - break; - } - - mm = vi->mm; - down_read(&mm->mmap_sem); - vma = find_vma(mm, (unsigned long)vi->vaddr); - if (!vma || !valid_vma(vma, is_register)) { - list_del(&vi->probe_list); - kfree(vi); - up_read(&mm->mmap_sem); - mmput(mm); - continue; - } - vaddr = vma_address(vma, uprobe->offset); - if (vma->vm_file->f_mapping->host != uprobe->inode || - vaddr != vi->vaddr) { - list_del(&vi->probe_list); - kfree(vi); - up_read(&mm->mmap_sem); - mmput(mm); - continue; - } - - if (is_register) - ret = install_breakpoint(mm, uprobe, vma, vi->vaddr); - else - remove_breakpoint(mm, uprobe, vi->vaddr); - - up_read(&mm->mmap_sem); - mmput(mm); - if (is_register) { - if (ret && ret == -EEXIST) - ret = 0; - if (ret) - break; - } - } - - list_for_each_entry_safe(vi, tmpvi, &try_list, probe_list) { - list_del(&vi->probe_list); - kfree(vi); - } - - return ret; -} - -static int __uprobe_register(struct uprobe *uprobe) -{ - return register_for_each_vma(uprobe, true); -} - -static void __uprobe_unregister(struct uprobe *uprobe) -{ - if (!register_for_each_vma(uprobe, false)) - delete_uprobe(uprobe); - - /* TODO : cant unregister? schedule a worker thread */ -} - -/* - * uprobe_register - register a probe - * @inode: the file in which the probe has to be placed. - * @offset: offset from the start of the file. - * @consumer: information on howto handle the probe.. - * - * Apart from the access refcount, uprobe_register() takes a creation - * refcount (thro alloc_uprobe) if and only if this @uprobe is getting - * inserted into the rbtree (i.e first consumer for a @inode:@offset - * tuple). Creation refcount stops uprobe_unregister from freeing the - * @uprobe even before the register operation is complete. Creation - * refcount is released when the last @consumer for the @uprobe - * unregisters. - * - * Return errno if it cannot successully install probes - * else return 0 (success) - */ -int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) -{ - struct uprobe *uprobe; - int ret; - - if (!inode || !consumer || consumer->next) - return -EINVAL; - - if (offset > i_size_read(inode)) - return -EINVAL; - - ret = 0; - mutex_lock(uprobes_hash(inode)); - uprobe = alloc_uprobe(inode, offset); - - if (uprobe && !consumer_add(uprobe, consumer)) { - ret = __uprobe_register(uprobe); - if (ret) { - uprobe->consumers = NULL; - __uprobe_unregister(uprobe); - } else { - uprobe->flags |= UPROBES_RUN_HANDLER; - } - } - - mutex_unlock(uprobes_hash(inode)); - put_uprobe(uprobe); - - return ret; -} - -/* - * uprobe_unregister - unregister a already registered probe. - * @inode: the file in which the probe has to be removed. - * @offset: offset from the start of the file. - * @consumer: identify which probe if multiple probes are colocated. - */ -void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) -{ - struct uprobe *uprobe; - - if (!inode || !consumer) - return; - - uprobe = find_uprobe(inode, offset); - if (!uprobe) - return; - - mutex_lock(uprobes_hash(inode)); - - if (consumer_del(uprobe, consumer)) { - if (!uprobe->consumers) { - __uprobe_unregister(uprobe); - uprobe->flags &= ~UPROBES_RUN_HANDLER; - } - } - - mutex_unlock(uprobes_hash(inode)); - if (uprobe) - put_uprobe(uprobe); -} - -/* - * Of all the nodes that correspond to the given inode, return the node - * with the least offset. - */ -static struct rb_node *find_least_offset_node(struct inode *inode) -{ - struct uprobe u = { .inode = inode, .offset = 0}; - struct rb_node *n = uprobes_tree.rb_node; - struct rb_node *close_node = NULL; - struct uprobe *uprobe; - int match; - - while (n) { - uprobe = rb_entry(n, struct uprobe, rb_node); - match = match_uprobe(&u, uprobe); - - if (uprobe->inode == inode) - close_node = n; - - if (!match) - return close_node; - - if (match < 0) - n = n->rb_left; - else - n = n->rb_right; - } - - return close_node; -} - -/* - * For a given inode, build a list of probes that need to be inserted. - */ -static void build_probe_list(struct inode *inode, struct list_head *head) -{ - struct uprobe *uprobe; - unsigned long flags; - struct rb_node *n; - - spin_lock_irqsave(&uprobes_treelock, flags); - - n = find_least_offset_node(inode); - - for (; n; n = rb_next(n)) { - uprobe = rb_entry(n, struct uprobe, rb_node); - if (uprobe->inode != inode) - break; - - list_add(&uprobe->pending_list, head); - atomic_inc(&uprobe->ref); - } - - spin_unlock_irqrestore(&uprobes_treelock, flags); -} - -/* - * Called from mmap_region. - * called with mm->mmap_sem acquired. - * - * Return -ve no if we fail to insert probes and we cannot - * bail-out. - * Return 0 otherwise. i.e: - * - * - successful insertion of probes - * - (or) no possible probes to be inserted. - * - (or) insertion of probes failed but we can bail-out. - */ -int uprobe_mmap(struct vm_area_struct *vma) -{ - struct list_head tmp_list; - struct uprobe *uprobe, *u; - struct inode *inode; - int ret; - - if (!atomic_read(&uprobe_events) || !valid_vma(vma, true)) - return 0; - - inode = vma->vm_file->f_mapping->host; - if (!inode) - return 0; - - INIT_LIST_HEAD(&tmp_list); - mutex_lock(uprobes_mmap_hash(inode)); - build_probe_list(inode, &tmp_list); - - ret = 0; - - list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) { - loff_t vaddr; - - list_del(&uprobe->pending_list); - if (!ret) { - vaddr = vma_address(vma, uprobe->offset); - if (vaddr >= vma->vm_start && vaddr < vma->vm_end) { - ret = install_breakpoint(vma->vm_mm, uprobe, vma, vaddr); - /* Ignore double add: */ - if (ret == -EEXIST) - ret = 0; - } - } - put_uprobe(uprobe); - } - - mutex_unlock(uprobes_mmap_hash(inode)); - - return ret; -} - -static int __init init_uprobes(void) -{ - int i; - - for (i = 0; i < UPROBES_HASH_SZ; i++) { - mutex_init(&uprobes_mutex[i]); - mutex_init(&uprobes_mmap_mutex[i]); - } - return 0; -} - -static void __exit exit_uprobes(void) -{ -} - -module_init(init_uprobes); -module_exit(exit_uprobes); -- cgit v1.2.3 From 04a3d984d32e47983770d314cdb4e4d8f38fccb7 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Wed, 22 Feb 2012 14:45:35 +0530 Subject: uprobes/core: Make instruction tables volatile Some versions of gcc spits a warning about the asm operand for test_bit and also causes the first long of the instruction table to be output. Fix is similar to 7115e3fc on arch/x86/kernel/kprobes.c Signed-off-by: Srikar Dronamraju Cc: Peter Zijlstra Cc: Linus Torvalds Cc: Oleg Nesterov Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Masami Hiramatsu Cc: Anton Arapov Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Jiri Olsa Cc: Josh Stone Link: http://lkml.kernel.org/r/20120222091535.15880.12502.sendpatchset@srdronam.in.ibm.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/uprobes.c | 61 +++++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 29 deletions(-) (limited to 'arch') diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index cf2a18498425..13d616d6519b 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -53,34 +53,12 @@ (bc##UL << 0xc)|(bd##UL << 0xd)|(be##UL << 0xe)|(bf##UL << 0xf)) \ << (row % 32)) -#ifdef CONFIG_X86_64 -static u32 good_insns_64[256 / 32] = { - /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ - /* ---------------------------------------------- */ - W(0x00, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) | /* 00 */ - W(0x10, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) , /* 10 */ - W(0x20, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) | /* 20 */ - W(0x30, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) , /* 30 */ - W(0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 40 */ - W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */ - W(0x60, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* 60 */ - W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 70 */ - W(0x80, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */ - W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */ - W(0xa0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* a0 */ - W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* b0 */ - W(0xc0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0) | /* c0 */ - W(0xd0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */ - W(0xe0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* e0 */ - W(0xf0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1) /* f0 */ - /* ---------------------------------------------- */ - /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ -}; -#endif - -/* Good-instruction tables for 32-bit apps */ - -static u32 good_insns_32[256 / 32] = { +/* + * Good-instruction tables for 32-bit apps. This is non-const and volatile + * to keep gcc from statically optimizing it out, as variable_test_bit makes + * some versions of gcc to think only *(unsigned long*) is used. + */ +static volatile u32 good_insns_32[256 / 32] = { /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ /* ---------------------------------------------- */ W(0x00, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0) | /* 00 */ @@ -104,7 +82,7 @@ static u32 good_insns_32[256 / 32] = { }; /* Using this for both 64-bit and 32-bit apps */ -static u32 good_2byte_insns[256 / 32] = { +static volatile u32 good_2byte_insns[256 / 32] = { /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ /* ---------------------------------------------- */ W(0x00, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1) | /* 00 */ @@ -127,6 +105,31 @@ static u32 good_2byte_insns[256 / 32] = { /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ }; +#ifdef CONFIG_X86_64 +/* Good-instruction tables for 64-bit apps */ +static volatile u32 good_insns_64[256 / 32] = { + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ + /* ---------------------------------------------- */ + W(0x00, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) | /* 00 */ + W(0x10, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) , /* 10 */ + W(0x20, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) | /* 20 */ + W(0x30, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0) , /* 30 */ + W(0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) | /* 40 */ + W(0x50, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 50 */ + W(0x60, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* 60 */ + W(0x70, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 70 */ + W(0x80, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* 80 */ + W(0x90, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* 90 */ + W(0xa0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) | /* a0 */ + W(0xb0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* b0 */ + W(0xc0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0) | /* c0 */ + W(0xd0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1) , /* d0 */ + W(0xe0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0) | /* e0 */ + W(0xf0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1) /* f0 */ + /* ---------------------------------------------- */ + /* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +}; +#endif #undef W /* -- cgit v1.2.3 From 3ff54efdfaace9e9b2b7c1959a865be6b91de96c Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Wed, 22 Feb 2012 14:46:02 +0530 Subject: uprobes/core: Move insn to arch specific structure Few cleanups suggested by Ingo Molnar. - Rename struct uprobe_arch_info to struct arch_uprobe. - Move insn from struct uprobe to struct arch_uprobe. - Make arch specific uprobe functions to accept struct arch_uprobe instead of struct uprobe. - Move struct uprobe to kernel/uprobes.c from include/linux/uprobes.h Signed-off-by: Srikar Dronamraju Cc: Peter Zijlstra Cc: Linus Torvalds Cc: Oleg Nesterov Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Masami Hiramatsu Cc: Anton Arapov Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Jiri Olsa Cc: Josh Stone Link: http://lkml.kernel.org/r/20120222091602.15880.40249.sendpatchset@srdronam.in.ibm.com [ Made various small improvements ] Signed-off-by: Ingo Molnar --- arch/x86/include/asm/uprobes.h | 6 ++--- arch/x86/kernel/uprobes.c | 60 +++++++++++++++++++++--------------------- include/linux/uprobes.h | 23 ++-------------- kernel/events/uprobes.c | 38 +++++++++++++++++--------- 4 files changed, 61 insertions(+), 66 deletions(-) (limited to 'arch') diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index 072df3902636..f7ce310a429d 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h @@ -31,13 +31,13 @@ typedef u8 uprobe_opcode_t; #define UPROBES_BKPT_INSN 0xcc #define UPROBES_BKPT_INSN_SIZE 1 -struct uprobe_arch_info { +struct arch_uprobe { u16 fixups; + u8 insn[MAX_UINSN_BYTES]; #ifdef CONFIG_X86_64 unsigned long rip_rela_target_address; #endif }; -struct uprobe; -extern int arch_uprobes_analyze_insn(struct mm_struct *mm, struct uprobe *uprobe); +extern int arch_uprobes_analyze_insn(struct mm_struct *mm, struct arch_uprobe *arch_uprobe); #endif /* _ASM_UPROBES_H */ diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 13d616d6519b..04dfcef2d028 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -200,9 +200,9 @@ static bool is_prefix_bad(struct insn *insn) return false; } -static int validate_insn_32bits(struct uprobe *uprobe, struct insn *insn) +static int validate_insn_32bits(struct arch_uprobe *auprobe, struct insn *insn) { - insn_init(insn, uprobe->insn, false); + insn_init(insn, auprobe->insn, false); /* Skip good instruction prefixes; reject "bad" ones. */ insn_get_opcode(insn); @@ -222,11 +222,11 @@ static int validate_insn_32bits(struct uprobe *uprobe, struct insn *insn) /* * Figure out which fixups post_xol() will need to perform, and annotate - * uprobe->arch_info.fixups accordingly. To start with, - * uprobe->arch_info.fixups is either zero or it reflects rip-related + * arch_uprobe->fixups accordingly. To start with, + * arch_uprobe->fixups is either zero or it reflects rip-related * fixups. */ -static void prepare_fixups(struct uprobe *uprobe, struct insn *insn) +static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn) { bool fix_ip = true, fix_call = false; /* defaults */ int reg; @@ -269,17 +269,17 @@ static void prepare_fixups(struct uprobe *uprobe, struct insn *insn) break; } if (fix_ip) - uprobe->arch_info.fixups |= UPROBES_FIX_IP; + auprobe->fixups |= UPROBES_FIX_IP; if (fix_call) - uprobe->arch_info.fixups |= UPROBES_FIX_CALL; + auprobe->fixups |= UPROBES_FIX_CALL; } #ifdef CONFIG_X86_64 /* - * If uprobe->insn doesn't use rip-relative addressing, return + * If arch_uprobe->insn doesn't use rip-relative addressing, return * immediately. Otherwise, rewrite the instruction so that it accesses * its memory operand indirectly through a scratch register. Set - * uprobe->arch_info.fixups and uprobe->arch_info.rip_rela_target_address + * arch_uprobe->fixups and arch_uprobe->rip_rela_target_address * accordingly. (The contents of the scratch register will be saved * before we single-step the modified instruction, and restored * afterward.) @@ -297,7 +297,7 @@ static void prepare_fixups(struct uprobe *uprobe, struct insn *insn) * - There's never a SIB byte. * - The displacement is always 4 bytes. */ -static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) +static void handle_riprel_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) { u8 *cursor; u8 reg; @@ -305,7 +305,7 @@ static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, stru if (mm->context.ia32_compat) return; - uprobe->arch_info.rip_rela_target_address = 0x0; + auprobe->rip_rela_target_address = 0x0; if (!insn_rip_relative(insn)) return; @@ -315,7 +315,7 @@ static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, stru * we want to encode rax/rcx, not r8/r9. */ if (insn->rex_prefix.nbytes) { - cursor = uprobe->insn + insn_offset_rex_prefix(insn); + cursor = auprobe->insn + insn_offset_rex_prefix(insn); *cursor &= 0xfe; /* Clearing REX.B bit */ } @@ -324,7 +324,7 @@ static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, stru * displacement. Beyond the displacement, for some instructions, * is the immediate operand. */ - cursor = uprobe->insn + insn_offset_modrm(insn); + cursor = auprobe->insn + insn_offset_modrm(insn); insn_get_length(insn); /* @@ -341,18 +341,18 @@ static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, stru * is NOT the register operand, so we use %rcx (register * #1) for the scratch register. */ - uprobe->arch_info.fixups = UPROBES_FIX_RIP_CX; + auprobe->fixups = UPROBES_FIX_RIP_CX; /* Change modrm from 00 000 101 to 00 000 001. */ *cursor = 0x1; } else { /* Use %rax (register #0) for the scratch register. */ - uprobe->arch_info.fixups = UPROBES_FIX_RIP_AX; + auprobe->fixups = UPROBES_FIX_RIP_AX; /* Change modrm from 00 xxx 101 to 00 xxx 000 */ *cursor = (reg << 3); } /* Target address = address of next instruction + (signed) offset */ - uprobe->arch_info.rip_rela_target_address = (long)insn->length + insn->displacement.value; + auprobe->rip_rela_target_address = (long)insn->length + insn->displacement.value; /* Displacement field is gone; slide immediate field (if any) over. */ if (insn->immediate.nbytes) { @@ -362,9 +362,9 @@ static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, stru return; } -static int validate_insn_64bits(struct uprobe *uprobe, struct insn *insn) +static int validate_insn_64bits(struct arch_uprobe *auprobe, struct insn *insn) { - insn_init(insn, uprobe->insn, true); + insn_init(insn, auprobe->insn, true); /* Skip good instruction prefixes; reject "bad" ones. */ insn_get_opcode(insn); @@ -381,42 +381,42 @@ static int validate_insn_64bits(struct uprobe *uprobe, struct insn *insn) return -ENOTSUPP; } -static int validate_insn_bits(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) +static int validate_insn_bits(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) { if (mm->context.ia32_compat) - return validate_insn_32bits(uprobe, insn); - return validate_insn_64bits(uprobe, insn); + return validate_insn_32bits(auprobe, insn); + return validate_insn_64bits(auprobe, insn); } #else /* 32-bit: */ -static void handle_riprel_insn(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) +static void handle_riprel_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) { /* No RIP-relative addressing on 32-bit */ } -static int validate_insn_bits(struct mm_struct *mm, struct uprobe *uprobe, struct insn *insn) +static int validate_insn_bits(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) { - return validate_insn_32bits(uprobe, insn); + return validate_insn_32bits(auprobe, insn); } #endif /* CONFIG_X86_64 */ /** * arch_uprobes_analyze_insn - instruction analysis including validity and fixups. * @mm: the probed address space. - * @uprobe: the probepoint information. + * @arch_uprobe: the probepoint information. * Return 0 on success or a -ve number on error. */ -int arch_uprobes_analyze_insn(struct mm_struct *mm, struct uprobe *uprobe) +int arch_uprobes_analyze_insn(struct mm_struct *mm, struct arch_uprobe *auprobe) { int ret; struct insn insn; - uprobe->arch_info.fixups = 0; - ret = validate_insn_bits(mm, uprobe, &insn); + auprobe->fixups = 0; + ret = validate_insn_bits(mm, auprobe, &insn); if (ret != 0) return ret; - handle_riprel_insn(mm, uprobe, &insn); - prepare_fixups(uprobe, &insn); + handle_riprel_insn(mm, auprobe, &insn); + prepare_fixups(auprobe, &insn); return 0; } diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index fd45b70750d4..9c6be62787ed 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -29,12 +29,6 @@ struct vm_area_struct; #ifdef CONFIG_ARCH_SUPPORTS_UPROBES #include -#else - -typedef u8 uprobe_opcode_t; -struct uprobe_arch_info {}; - -#define MAX_UINSN_BYTES 4 #endif /* flags that denote/change uprobes behaviour */ @@ -56,22 +50,9 @@ struct uprobe_consumer { struct uprobe_consumer *next; }; -struct uprobe { - struct rb_node rb_node; /* node in the rb tree */ - atomic_t ref; - struct rw_semaphore consumer_rwsem; - struct list_head pending_list; - struct uprobe_arch_info arch_info; - struct uprobe_consumer *consumers; - struct inode *inode; /* Also hold a ref to inode */ - loff_t offset; - int flags; - u8 insn[MAX_UINSN_BYTES]; -}; - #ifdef CONFIG_UPROBES -extern int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr); -extern int __weak set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr, bool verify); +extern int __weak set_bkpt(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr); +extern int __weak set_orig_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr, bool verify); extern bool __weak is_bkpt_insn(uprobe_opcode_t *insn); extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer); extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer); diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index ee496ad95db3..13f1b5909af4 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -65,6 +65,18 @@ struct vma_info { loff_t vaddr; }; +struct uprobe { + struct rb_node rb_node; /* node in the rb tree */ + atomic_t ref; + struct rw_semaphore consumer_rwsem; + struct list_head pending_list; + struct uprobe_consumer *consumers; + struct inode *inode; /* Also hold a ref to inode */ + loff_t offset; + int flags; + struct arch_uprobe arch; +}; + /* * valid_vma: Verify if the specified vma is an executable vma * Relax restrictions while unregistering: vm_flags might have @@ -180,7 +192,7 @@ bool __weak is_bkpt_insn(uprobe_opcode_t *insn) /* * write_opcode - write the opcode at a given virtual address. * @mm: the probed process address space. - * @uprobe: the breakpointing information. + * @arch_uprobe: the breakpointing information. * @vaddr: the virtual address to store the opcode. * @opcode: opcode to be written at @vaddr. * @@ -190,13 +202,14 @@ bool __weak is_bkpt_insn(uprobe_opcode_t *insn) * For mm @mm, write the opcode at @vaddr. * Return 0 (success) or a negative errno. */ -static int write_opcode(struct mm_struct *mm, struct uprobe *uprobe, +static int write_opcode(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr, uprobe_opcode_t opcode) { struct page *old_page, *new_page; struct address_space *mapping; void *vaddr_old, *vaddr_new; struct vm_area_struct *vma; + struct uprobe *uprobe; loff_t addr; int ret; @@ -216,6 +229,7 @@ static int write_opcode(struct mm_struct *mm, struct uprobe *uprobe, if (!valid_vma(vma, is_bkpt_insn(&opcode))) goto put_out; + uprobe = container_of(auprobe, struct uprobe, arch); mapping = uprobe->inode->i_mapping; if (mapping != vma->vm_file->f_mapping) goto put_out; @@ -326,7 +340,7 @@ static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) * For mm @mm, store the breakpoint instruction at @vaddr. * Return 0 (success) or a negative errno. */ -int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr) +int __weak set_bkpt(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr) { int result; @@ -337,7 +351,7 @@ int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, unsigned long v if (result) return result; - return write_opcode(mm, uprobe, vaddr, UPROBES_BKPT_INSN); + return write_opcode(mm, auprobe, vaddr, UPROBES_BKPT_INSN); } /** @@ -351,7 +365,7 @@ int __weak set_bkpt(struct mm_struct *mm, struct uprobe *uprobe, unsigned long v * Return 0 (success) or a negative errno. */ int __weak -set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr, bool verify) +set_orig_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr, bool verify) { if (verify) { int result; @@ -363,7 +377,7 @@ set_orig_insn(struct mm_struct *mm, struct uprobe *uprobe, unsigned long vaddr, if (result != 1) return result; } - return write_opcode(mm, uprobe, vaddr, *(uprobe_opcode_t *)uprobe->insn); + return write_opcode(mm, auprobe, vaddr, *(uprobe_opcode_t *)auprobe->insn); } static int match_uprobe(struct uprobe *l, struct uprobe *r) @@ -593,13 +607,13 @@ static int copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned /* Instruction at the page-boundary; copy bytes in second page */ if (nbytes < bytes) { - if (__copy_insn(mapping, vma, uprobe->insn + nbytes, + if (__copy_insn(mapping, vma, uprobe->arch.insn + nbytes, bytes - nbytes, uprobe->offset + nbytes)) return -ENOMEM; bytes = nbytes; } - return __copy_insn(mapping, vma, uprobe->insn, bytes, uprobe->offset); + return __copy_insn(mapping, vma, uprobe->arch.insn, bytes, uprobe->offset); } static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, @@ -625,23 +639,23 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, if (ret) return ret; - if (is_bkpt_insn((uprobe_opcode_t *)uprobe->insn)) + if (is_bkpt_insn((uprobe_opcode_t *)uprobe->arch.insn)) return -EEXIST; - ret = arch_uprobes_analyze_insn(mm, uprobe); + ret = arch_uprobes_analyze_insn(mm, &uprobe->arch); if (ret) return ret; uprobe->flags |= UPROBES_COPY_INSN; } - ret = set_bkpt(mm, uprobe, addr); + ret = set_bkpt(mm, &uprobe->arch, addr); return ret; } static void remove_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, loff_t vaddr) { - set_orig_insn(mm, uprobe, (unsigned long)vaddr, true); + set_orig_insn(mm, &uprobe->arch, (unsigned long)vaddr, true); } static void delete_uprobe(struct uprobe *uprobe) -- cgit v1.2.3 From e9b57cca3dbdc7a0b90514af8bf613baf97105a5 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 28 Feb 2012 16:08:02 -0500 Subject: sparc: Use vsprintf extention %pf with builtin_return_address Emit the function name not the address when possible. builtin_return_address() gives an address. When building a kernel with CONFIG_KALLSYMS, emit the actual function name not the address. Signed-off-by: Joe Perches Signed-off-by: David S. Miller --- arch/sparc/kernel/ds.c | 2 +- arch/sparc/mm/srmmu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/sparc/kernel/ds.c b/arch/sparc/kernel/ds.c index 381edcd5bc29..e65b9fc52b49 100644 --- a/arch/sparc/kernel/ds.c +++ b/arch/sparc/kernel/ds.c @@ -868,7 +868,7 @@ void ldom_power_off(void) static void ds_conn_reset(struct ds_info *dp) { - printk(KERN_ERR "ds-%llu: ds_conn_reset() from %p\n", + printk(KERN_ERR "ds-%llu: ds_conn_reset() from %pf\n", dp->id, __builtin_return_address(0)); } diff --git a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c index cbef74e793b8..f37fbb24458b 100644 --- a/arch/sparc/mm/srmmu.c +++ b/arch/sparc/mm/srmmu.c @@ -705,7 +705,7 @@ static void swift_update_mmu_cache(struct vm_area_struct * vma, unsigned long ad val = srmmu_hwprobe(address); if (val != 0 && pte_val(*ptep) != val) { printk("swift_update_mmu_cache: " - "addr %lx put %08x probed %08x from %p\n", + "addr %lx put %08x probed %08x from %pf\n", address, pte_val(*ptep), val, __builtin_return_address(0)); srmmu_flush_whole_tlb(); -- cgit v1.2.3 From 722bc6b16771ed80871e1fd81c86d3627dda2ac8 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Mon, 5 Mar 2012 15:05:13 -0800 Subject: x86/mm: Fix the size calculation of mapping tables For machines that enable PSE, the first 2/4M memory region still uses 4K pages, so needs more PTEs in this case, but find_early_table_space() doesn't count this. This patch fixes it. The bug was found via code review, no misbehavior of the kernel was observed. Signed-off-by: WANG Cong Cc: Yinghai Lu Cc: Tejun Heo Cc: Signed-off-by: Andrew Morton Link: http://lkml.kernel.org/n/tip-kq6a00qe33h7c7ais2xsywnh@git.kernel.org Signed-off-by: Ingo Molnar --- arch/x86/mm/init.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'arch') diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index 6cabf6570d64..2e92fdcbea86 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -30,8 +30,14 @@ int direct_gbpages #endif ; -static void __init find_early_table_space(unsigned long end, int use_pse, - int use_gbpages) +struct map_range { + unsigned long start; + unsigned long end; + unsigned page_size_mask; +}; + +static void __init find_early_table_space(struct map_range *mr, unsigned long end, + int use_pse, int use_gbpages) { unsigned long puds, pmds, ptes, tables, start = 0, good_end = end; phys_addr_t base; @@ -56,6 +62,9 @@ static void __init find_early_table_space(unsigned long end, int use_pse, #ifdef CONFIG_X86_32 extra += PMD_SIZE; #endif + /* The first 2/4M doesn't use large pages. */ + extra += mr->end - mr->start; + ptes = (extra + PAGE_SIZE - 1) >> PAGE_SHIFT; } else ptes = (end + PAGE_SIZE - 1) >> PAGE_SHIFT; @@ -85,12 +94,6 @@ void __init native_pagetable_reserve(u64 start, u64 end) memblock_reserve(start, end - start); } -struct map_range { - unsigned long start; - unsigned long end; - unsigned page_size_mask; -}; - #ifdef CONFIG_X86_32 #define NR_RANGE_MR 3 #else /* CONFIG_X86_64 */ @@ -262,7 +265,7 @@ unsigned long __init_refok init_memory_mapping(unsigned long start, * nodes are discovered. */ if (!after_bootmem) - find_early_table_space(end, use_pse, use_gbpages); + find_early_table_space(&mr[0], end, use_pse, use_gbpages); for (i = 0; i < nr_range; i++) ret = kernel_physical_mapping_init(mr[i].start, mr[i].end, -- cgit v1.2.3 From f9d29f1617eb1b2f1fd41622bd1a0fc51658d2f0 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Mon, 20 Feb 2012 12:26:06 +0200 Subject: arm: omap3: voltage: fix channel configuration OMAP3 uses the default settings for VDD1 channel, otherwise the settings will overlap with VDD2 and attempting to modify VDD1 voltage will actually change VDD2 voltage. Signed-off-by: Tero Kristo Reviewed-by: Kevin Hilman Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/vc3xxx_data.c | 1 + 1 file changed, 1 insertion(+) (limited to 'arch') diff --git a/arch/arm/mach-omap2/vc3xxx_data.c b/arch/arm/mach-omap2/vc3xxx_data.c index a5ec7f8f2ea8..5d8eaf31569c 100644 --- a/arch/arm/mach-omap2/vc3xxx_data.c +++ b/arch/arm/mach-omap2/vc3xxx_data.c @@ -46,6 +46,7 @@ static struct omap_vc_common omap3_vc_common = { }; struct omap_vc_channel omap3_vc_mpu = { + .flags = OMAP_VC_CHANNEL_DEFAULT, .common = &omap3_vc_common, .smps_sa_reg = OMAP3_PRM_VC_SMPS_SA_OFFSET, .smps_volra_reg = OMAP3_PRM_VC_SMPS_VOL_RA_OFFSET, -- cgit v1.2.3 From 23e22a5ed4a5ad26ef89c30decd414024f871664 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Mon, 20 Feb 2012 12:26:07 +0200 Subject: arm: omap3: add common twl configurations for vdd1 and vdd2 VDD1 and VDD2 are the core voltage regulators on OMAP3. VDD1 is used to control MPU/IVA voltage, and VDD2 is used for CORE. These regulators are needed by DVFS. Voltage ranges for VDD1 and VDD2 are taken from twl4030/twl5030 data manuals: - SWCS019L : TWL4030 ES3.1 Data Manual rev L - SWCS030E : TWL5030 ES1.2 Data Manual rev E Signed-off-by: Tero Kristo Reviewed-by: Kevin Hilman Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/twl-common.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'arch') diff --git a/arch/arm/mach-omap2/twl-common.c b/arch/arm/mach-omap2/twl-common.c index 10b20c652e5d..913d6c3c4cb6 100644 --- a/arch/arm/mach-omap2/twl-common.c +++ b/arch/arm/mach-omap2/twl-common.c @@ -126,6 +126,38 @@ static struct regulator_init_data omap3_vpll2_idata = { .consumer_supplies = omap3_vpll2_supplies, }; +static struct regulator_consumer_supply omap3_vdd1_supply[] = { + REGULATOR_SUPPLY("vcc", "mpu.0"), +}; + +static struct regulator_consumer_supply omap3_vdd2_supply[] = { + REGULATOR_SUPPLY("vcc", "l3_main.0"), +}; + +static struct regulator_init_data omap3_vdd1 = { + .constraints = { + .name = "vdd_mpu_iva", + .min_uV = 600000, + .max_uV = 1450000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + }, + .num_consumer_supplies = ARRAY_SIZE(omap3_vdd1_supply), + .consumer_supplies = omap3_vdd1_supply, +}; + +static struct regulator_init_data omap3_vdd2 = { + .constraints = { + .name = "vdd_core", + .min_uV = 600000, + .max_uV = 1450000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + }, + .num_consumer_supplies = ARRAY_SIZE(omap3_vdd2_supply), + .consumer_supplies = omap3_vdd2_supply, +}; + void __init omap3_pmic_get_config(struct twl4030_platform_data *pmic_data, u32 pdata_flags, u32 regulators_flags) { @@ -133,6 +165,10 @@ void __init omap3_pmic_get_config(struct twl4030_platform_data *pmic_data, pmic_data->irq_base = TWL4030_IRQ_BASE; if (!pmic_data->irq_end) pmic_data->irq_end = TWL4030_IRQ_END; + if (!pmic_data->vdd1) + pmic_data->vdd1 = &omap3_vdd1; + if (!pmic_data->vdd2) + pmic_data->vdd2 = &omap3_vdd2; /* Common platform data configurations */ if (pdata_flags & TWL_COMMON_PDATA_USB && !pmic_data->usb) -- cgit v1.2.3 From 49c008ecce1f9a549c12e8957668d60008ab0d79 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Mon, 20 Feb 2012 12:26:08 +0200 Subject: arm: omap3: twl: add external controllers for core voltage regulators VDD1 and VDD2 now use voltage processor for controlling the regulators. This is done by passing additional voltdm data during the regulator init. Signed-off-by: Tero Kristo Reviewed-by: Kevin Hilman Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/twl-common.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-omap2/twl-common.c b/arch/arm/mach-omap2/twl-common.c index 913d6c3c4cb6..618fefb2b9b9 100644 --- a/arch/arm/mach-omap2/twl-common.c +++ b/arch/arm/mach-omap2/twl-common.c @@ -31,12 +31,25 @@ #include "twl-common.h" #include "pm.h" +#include "voltage.h" static struct i2c_board_info __initdata pmic_i2c_board_info = { .addr = 0x48, .flags = I2C_CLIENT_WAKE, }; +static int twl_set_voltage(void *data, int target_uV) +{ + struct voltagedomain *voltdm = (struct voltagedomain *)data; + return voltdm_scale(voltdm, target_uV); +} + +static int twl_get_voltage(void *data) +{ + struct voltagedomain *voltdm = (struct voltagedomain *)data; + return voltdm_get_voltage(voltdm); +} + void __init omap_pmic_init(int bus, u32 clkrate, const char *pmic_type, int pmic_irq, struct twl4030_platform_data *pmic_data) @@ -158,6 +171,16 @@ static struct regulator_init_data omap3_vdd2 = { .consumer_supplies = omap3_vdd2_supply, }; +static struct twl_regulator_driver_data omap3_vdd1_drvdata = { + .get_voltage = twl_get_voltage, + .set_voltage = twl_set_voltage, +}; + +static struct twl_regulator_driver_data omap3_vdd2_drvdata = { + .get_voltage = twl_get_voltage, + .set_voltage = twl_set_voltage, +}; + void __init omap3_pmic_get_config(struct twl4030_platform_data *pmic_data, u32 pdata_flags, u32 regulators_flags) { @@ -165,10 +188,16 @@ void __init omap3_pmic_get_config(struct twl4030_platform_data *pmic_data, pmic_data->irq_base = TWL4030_IRQ_BASE; if (!pmic_data->irq_end) pmic_data->irq_end = TWL4030_IRQ_END; - if (!pmic_data->vdd1) + if (!pmic_data->vdd1) { + omap3_vdd1.driver_data = &omap3_vdd1_drvdata; + omap3_vdd1_drvdata.data = voltdm_lookup("mpu_iva"); pmic_data->vdd1 = &omap3_vdd1; - if (!pmic_data->vdd2) + } + if (!pmic_data->vdd2) { + omap3_vdd2.driver_data = &omap3_vdd2_drvdata; + omap3_vdd2_drvdata.data = voltdm_lookup("core"); pmic_data->vdd2 = &omap3_vdd2; + } /* Common platform data configurations */ if (pdata_flags & TWL_COMMON_PDATA_USB && !pmic_data->usb) -- cgit v1.2.3 From e160dda0f49f54deddd62e943e449a13430c2e8b Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Wed, 22 Feb 2012 12:40:40 +0200 Subject: arm: omap4: add common twl configurations for vdd1, vdd2 and vdd3 VDD1 is used for MPU voltage, VDD2 is for IVA and VDD3 is for CORE. These are needed by DVFS. This patch also adds external controller support for these regulators, the default I2C control channel can't be used to modify the voltages for these regulators on TWL6030. Signed-off-by: Tero Kristo Reviewed-by: Kevin Hilman Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/twl-common.c | 82 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) (limited to 'arch') diff --git a/arch/arm/mach-omap2/twl-common.c b/arch/arm/mach-omap2/twl-common.c index 618fefb2b9b9..1ddad222361a 100644 --- a/arch/arm/mach-omap2/twl-common.c +++ b/arch/arm/mach-omap2/twl-common.c @@ -349,6 +349,70 @@ static struct regulator_init_data omap4_clk32kg_idata = { }, }; +static struct regulator_consumer_supply omap4_vdd1_supply[] = { + REGULATOR_SUPPLY("vcc", "mpu.0"), +}; + +static struct regulator_consumer_supply omap4_vdd2_supply[] = { + REGULATOR_SUPPLY("vcc", "iva.0"), +}; + +static struct regulator_consumer_supply omap4_vdd3_supply[] = { + REGULATOR_SUPPLY("vcc", "l3_main.0"), +}; + +static struct regulator_init_data omap4_vdd1 = { + .constraints = { + .name = "vdd_mpu", + .min_uV = 500000, + .max_uV = 1500000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + }, + .num_consumer_supplies = ARRAY_SIZE(omap4_vdd1_supply), + .consumer_supplies = omap4_vdd1_supply, +}; + +static struct regulator_init_data omap4_vdd2 = { + .constraints = { + .name = "vdd_iva", + .min_uV = 500000, + .max_uV = 1500000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + }, + .num_consumer_supplies = ARRAY_SIZE(omap4_vdd2_supply), + .consumer_supplies = omap4_vdd2_supply, +}; + +static struct regulator_init_data omap4_vdd3 = { + .constraints = { + .name = "vdd_core", + .min_uV = 500000, + .max_uV = 1500000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + }, + .num_consumer_supplies = ARRAY_SIZE(omap4_vdd3_supply), + .consumer_supplies = omap4_vdd3_supply, +}; + + +static struct twl_regulator_driver_data omap4_vdd1_drvdata = { + .get_voltage = twl_get_voltage, + .set_voltage = twl_set_voltage, +}; + +static struct twl_regulator_driver_data omap4_vdd2_drvdata = { + .get_voltage = twl_get_voltage, + .set_voltage = twl_set_voltage, +}; + +static struct twl_regulator_driver_data omap4_vdd3_drvdata = { + .get_voltage = twl_get_voltage, + .set_voltage = twl_set_voltage, +}; + void __init omap4_pmic_get_config(struct twl4030_platform_data *pmic_data, u32 pdata_flags, u32 regulators_flags) { @@ -357,6 +421,24 @@ void __init omap4_pmic_get_config(struct twl4030_platform_data *pmic_data, if (!pmic_data->irq_end) pmic_data->irq_end = TWL6030_IRQ_END; + if (!pmic_data->vdd1) { + omap4_vdd1.driver_data = &omap4_vdd1_drvdata; + omap4_vdd1_drvdata.data = voltdm_lookup("mpu"); + pmic_data->vdd1 = &omap4_vdd1; + } + + if (!pmic_data->vdd2) { + omap4_vdd2.driver_data = &omap4_vdd2_drvdata; + omap4_vdd2_drvdata.data = voltdm_lookup("iva"); + pmic_data->vdd2 = &omap4_vdd2; + } + + if (!pmic_data->vdd3) { + omap4_vdd3.driver_data = &omap4_vdd3_drvdata; + omap4_vdd3_drvdata.data = voltdm_lookup("core"); + pmic_data->vdd3 = &omap4_vdd3; + } + /* Common platform data configurations */ if (pdata_flags & TWL_COMMON_PDATA_USB && !pmic_data->usb) pmic_data->usb = &omap4_usb_pdata; -- cgit v1.2.3 From c15f1d84bb3ddd668593e9bca53221a2f82e9e99 Mon Sep 17 00:00:00 2001 From: Kevin Hilman Date: Fri, 2 Mar 2012 14:08:57 -0800 Subject: ARM: OMAP2+: voltage: ensure voltage used is exact voltage from OPP table When using the SMPS regulators to scale voltages, the regulator framework may pass a minimum voltage that is not an exact OPP voltage. For the VC/VP controlled voltage domains, we must ensure that the voltage requested is the exact voltage from the OPP table. This is especially critical when using SR. To fix, voltdm_scale() uses the target voltage passed to walk through the OPP voltages until it finds a voltage that is >= one of the OPP voltages. Cc: Tero Kristo Cc: Nishanth Menon Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/voltage.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-omap2/voltage.c b/arch/arm/mach-omap2/voltage.c index 8a36342e60d2..4dc60e83e00d 100644 --- a/arch/arm/mach-omap2/voltage.c +++ b/arch/arm/mach-omap2/voltage.c @@ -73,7 +73,8 @@ unsigned long voltdm_get_voltage(struct voltagedomain *voltdm) int voltdm_scale(struct voltagedomain *voltdm, unsigned long target_volt) { - int ret; + int ret, i; + unsigned long volt = 0; if (!voltdm || IS_ERR(voltdm)) { pr_warning("%s: VDD specified does not exist!\n", __func__); @@ -86,9 +87,23 @@ int voltdm_scale(struct voltagedomain *voltdm, return -ENODATA; } - ret = voltdm->scale(voltdm, target_volt); + /* Adjust voltage to the exact voltage from the OPP table */ + for (i = 0; voltdm->volt_data[i].volt_nominal != 0; i++) { + if (voltdm->volt_data[i].volt_nominal >= target_volt) { + volt = voltdm->volt_data[i].volt_nominal; + break; + } + } + + if (!volt) { + pr_warning("%s: not scaling. OPP voltage for %lu, not found.\n", + __func__, target_volt); + return -EINVAL; + } + + ret = voltdm->scale(voltdm, volt); if (!ret) - voltdm->nominal_volt = target_volt; + voltdm->nominal_volt = volt; return ret; } -- cgit v1.2.3 From 4711244c764da806fc9d14793c0664f3709ef17d Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Thu, 15 Dec 2011 15:46:47 +0100 Subject: arm/dts: twl6030: Add DTS file for twl6030 PMIC Add a dedicated DTS file for the twl6030 Power IC. This chip is a big SoC that will be reused in a lot of various OMAP4+ boards. Note: This file is supposed to be included in a board DTS that will create the twl node in order to allow the &twl reference to work. Exmaple: ... &i2c1 { twl: twl@48 { reg = <0x48>; interrupts = <0 7 4>; interrupt-controller; interrupt-parent = <&gic>; }; }; /include/ "twl6030.dtsi" ... Signed-off-by: Benoit Cousson Acked-by: Grant Likely Acked-by: Rob Herring --- arch/arm/boot/dts/twl6030.dtsi | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 arch/arm/boot/dts/twl6030.dtsi (limited to 'arch') diff --git a/arch/arm/boot/dts/twl6030.dtsi b/arch/arm/boot/dts/twl6030.dtsi new file mode 100644 index 000000000000..b7b4e5e4340a --- /dev/null +++ b/arch/arm/boot/dts/twl6030.dtsi @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Integrated Power Management Chip + * http://www.ti.com/lit/ds/symlink/twl6030.pdf + */ +&twl { + compatible = "ti,twl6030"; + interrupt-controller; + #interrupt-cells = <1>; + + rtc { + compatible = "ti,twl4030-rtc"; + interrupts = <11>; + }; +}; -- cgit v1.2.3 From dce90d596de288ebb9d2a4e01f996b599c3ed3ff Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Thu, 15 Dec 2011 15:47:41 +0100 Subject: arm/dts: twl4030: Add DTS file for twl4030 PM + Audio IC Add a dedicated DTS file for the twl4030/5030 Power + Audio IC. This chip is a big SoC that will be reused in a lot of various OMAP3 boards. Signed-off-by: Benoit Cousson Acked-by: Grant Likely Acked-by: Rob Herring --- arch/arm/boot/dts/twl4030.dtsi | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 arch/arm/boot/dts/twl4030.dtsi (limited to 'arch') diff --git a/arch/arm/boot/dts/twl4030.dtsi b/arch/arm/boot/dts/twl4030.dtsi new file mode 100644 index 000000000000..8be5223e40eb --- /dev/null +++ b/arch/arm/boot/dts/twl4030.dtsi @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* + * Integrated Power Management Chip + */ +&twl { + compatible = "ti,twl4030"; + interrupt-controller; + #interrupt-cells = <1>; + + rtc { + compatible = "ti,twl4030-rtc"; + interrupts = <11>; + }; +}; -- cgit v1.2.3 From 898ee397c31a4f057c2f3a200784d62bd64e83b0 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Wed, 17 Aug 2011 18:42:25 +0530 Subject: arm/dts: omap4-panda: Add twl6030 and i2c EEPROM Update pandaboard dts file with required clock frequencies for the i2c client devices existing on pandaboard. Add the twl6030 node in i2c1 controller. This is the minimal support needed to boot OMAP4 boards without any crash. The support for all the features included in this MFD will be added later. Add a generic i2c EEPROM entry. Signed-off-by: Benoit Cousson Acked-by: Grant Likely Acked-by: Rob Herring --- arch/arm/boot/dts/omap4-panda.dts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) (limited to 'arch') diff --git a/arch/arm/boot/dts/omap4-panda.dts b/arch/arm/boot/dts/omap4-panda.dts index 9755ad5917f8..29646dc97229 100644 --- a/arch/arm/boot/dts/omap4-panda.dts +++ b/arch/arm/boot/dts/omap4-panda.dts @@ -18,3 +18,37 @@ reg = <0x80000000 0x40000000>; /* 1 GB */ }; }; + +&i2c1 { + clock-frequency = <400000>; + + twl: twl@48 { + reg = <0x48>; + /* SPI = 0, IRQ# = 7, 4 = active high level-sensitive */ + interrupts = <0 7 4>; /* IRQ_SYS_1N cascaded to gic */ + interrupt-parent = <&gic>; + }; +}; + +/include/ "twl6030.dtsi" + +&i2c2 { + clock-frequency = <400000>; +}; + +&i2c3 { + clock-frequency = <100000>; + + /* + * Display monitor features are burnt in their EEPROM as EDID data. + * The EEPROM is connected as I2C slave device. + */ + eeprom@50 { + compatible = "ti,eeprom"; + reg = <0x50>; + }; +}; + +&i2c4 { + clock-frequency = <400000>; +}; -- cgit v1.2.3 From 33632ae75080762b577a89952105245897e1c834 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Tue, 23 Aug 2011 17:17:46 +0200 Subject: arm/dts: omap4-sdp: Add twl6030, i2c3 and i2c4 devices Update DTS file with required clock frequencies for the i2c client devices existing on sdp4430. Add the twl6030 node inside the i2c1 controller node. This is the minimal support needed to boot OMAP4 boards without any crash. The support for all the features included in this MFD will be added later. Add the RTC submodule inside the twl node. Add tmp105 temperature sensor in i2c3 Add bh1780 Ambient Light Sensor in i2c3 Add hmc5843 3-Axis Digital Compass in i2c4 Signed-off-by: Benoit Cousson Acked-by: Grant Likely Acked-by: Rob Herring --- arch/arm/boot/dts/omap4-sdp.dts | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) (limited to 'arch') diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts index 63c6b2b2bf42..01db8b78ea71 100644 --- a/arch/arm/boot/dts/omap4-sdp.dts +++ b/arch/arm/boot/dts/omap4-sdp.dts @@ -18,3 +18,55 @@ reg = <0x80000000 0x40000000>; /* 1 GB */ }; }; + +&i2c1 { + clock-frequency = <400000>; + + twl: twl@48 { + reg = <0x48>; + /* SPI = 0, IRQ# = 7, 4 = active high level-sensitive */ + interrupts = <0 7 4>; /* IRQ_SYS_1N cascaded to gic */ + interrupt-parent = <&gic>; + }; +}; + +/include/ "twl6030.dtsi" + +&i2c2 { + clock-frequency = <400000>; +}; + +&i2c3 { + clock-frequency = <400000>; + + /* + * Temperature Sensor + * http://www.ti.com/lit/ds/symlink/tmp105.pdf + */ + tmp105@48 { + compatible = "ti,tmp105"; + reg = <0x48>; + }; + + /* + * Ambient Light Sensor + * http://www.rohm.com/products/databook/sensor/pdf/bh1780gli-e.pdf + */ + bh1780@29 { + compatible = "rohm,bh1780"; + reg = <0x29>; + }; +}; + +&i2c4 { + clock-frequency = <400000>; + + /* + * 3-Axis Digital Compass + * http://www.sparkfun.com/datasheets/Sensors/Magneto/HMC5843.pdf + */ + hmc5843@1e { + compatible = "honeywell,hmc5843"; + reg = <0x1e>; + }; +}; -- cgit v1.2.3 From 5340b51d78a245e01a890e85f4f0159593f90153 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Thu, 14 Jul 2011 01:44:54 +0530 Subject: arm/dts: omap3-beagle: Add twl4030 and i2c EEPROM Add required clock frequencies for the i2c client devices existing on beagle board. Add the twl4030 basic description with only the twl_rtc module. Add the EEPROM node. Based on original patch from Manju: http://www.spinics.net/lists/linux-omap/msg55831.html Signed-off-by: Benoit Cousson Acked-by: Grant Likely Acked-by: Rob Herring --- arch/arm/boot/dts/omap3-beagle.dts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'arch') diff --git a/arch/arm/boot/dts/omap3-beagle.dts b/arch/arm/boot/dts/omap3-beagle.dts index 9f72cd4cf308..714ba5d506ce 100644 --- a/arch/arm/boot/dts/omap3-beagle.dts +++ b/arch/arm/boot/dts/omap3-beagle.dts @@ -18,3 +18,32 @@ reg = <0x80000000 0x20000000>; /* 512 MB */ }; }; + +&i2c1 { + clock-frequency = <2600000>; + + twl: twl@48 { + reg = <0x48>; + interrupts = <7>; /* SYS_NIRQ cascaded to intc */ + interrupt-parent = <&intc>; + }; +}; + +/include/ "twl4030.dtsi" + +&i2c2 { + clock-frequency = <400000>; +}; + +&i2c3 { + clock-frequency = <100000>; + + /* + * Display monitor features are burnt in the EEPROM + * as EDID data. + */ + eeprom@50 { + compatible = "ti,eeprom"; + reg = <0x50>; + }; +}; -- cgit v1.2.3 From 510419435c6948fb32959d691bf84eaba41ca474 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 15 Dec 2011 17:56:36 +0100 Subject: perf/x86: Implement IBS event configuration This patch implements perf configuration for AMD IBS. The IBS pmu is selected using the type attribute in sysfs. There are two types of ibs pmus, for instruction fetch (IBS_FETCH) and for instruction execution (IBS_OP): /sys/bus/event_source/devices/ibs_fetch/type /sys/bus/event_source/devices/ibs_op/type Except for the sample period IBS can only be set up with raw config values and raw data samples. The event attributes for the syscall should be programmed like this (IBS_FETCH): type = get_pmu_type("/sys/bus/event_source/devices/ibs_fetch/type"); memset(&attr, 0, sizeof(attr)); attr.type = type; attr.sample_type = PERF_SAMPLE_CPU | PERF_SAMPLE_RAW; attr.config = IBS_FETCH_CONFIG_DEFAULT; This implementation does not yet support 64 bit counters. It is limited to the hardware counter bit width which is 20 bits. 64 bit support can be added later. Signed-off-by: Robert Richter Acked-by: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1323968199-9326-2-git-send-email-robert.richter@amd.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_amd_ibs.c | 92 +++++++++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 7 deletions(-) (limited to 'arch') diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c index 3b8a2d30d14e..36684eb248de 100644 --- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c +++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c @@ -16,12 +16,67 @@ static u32 ibs_caps; #if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD) -static struct pmu perf_ibs; +#define IBS_FETCH_CONFIG_MASK (IBS_FETCH_RAND_EN | IBS_FETCH_MAX_CNT) +#define IBS_OP_CONFIG_MASK IBS_OP_MAX_CNT + +struct perf_ibs { + struct pmu pmu; + unsigned int msr; + u64 config_mask; + u64 cnt_mask; + u64 enable_mask; +}; + +static struct perf_ibs perf_ibs_fetch; +static struct perf_ibs perf_ibs_op; + +static struct perf_ibs *get_ibs_pmu(int type) +{ + if (perf_ibs_fetch.pmu.type == type) + return &perf_ibs_fetch; + if (perf_ibs_op.pmu.type == type) + return &perf_ibs_op; + return NULL; +} static int perf_ibs_init(struct perf_event *event) { - if (perf_ibs.type != event->attr.type) + struct hw_perf_event *hwc = &event->hw; + struct perf_ibs *perf_ibs; + u64 max_cnt, config; + + perf_ibs = get_ibs_pmu(event->attr.type); + if (!perf_ibs) return -ENOENT; + + config = event->attr.config; + if (config & ~perf_ibs->config_mask) + return -EINVAL; + + if (hwc->sample_period) { + if (config & perf_ibs->cnt_mask) + /* raw max_cnt may not be set */ + return -EINVAL; + if (hwc->sample_period & 0x0f) + /* lower 4 bits can not be set in ibs max cnt */ + return -EINVAL; + max_cnt = hwc->sample_period >> 4; + if (max_cnt & ~perf_ibs->cnt_mask) + /* out of range */ + return -EINVAL; + config |= max_cnt; + } else { + max_cnt = config & perf_ibs->cnt_mask; + event->attr.sample_period = max_cnt << 4; + hwc->sample_period = event->attr.sample_period; + } + + if (!max_cnt) + return -EINVAL; + + hwc->config_base = perf_ibs->msr; + hwc->config = config; + return 0; } @@ -34,10 +89,32 @@ static void perf_ibs_del(struct perf_event *event, int flags) { } -static struct pmu perf_ibs = { - .event_init= perf_ibs_init, - .add= perf_ibs_add, - .del= perf_ibs_del, +static struct perf_ibs perf_ibs_fetch = { + .pmu = { + .task_ctx_nr = perf_invalid_context, + + .event_init = perf_ibs_init, + .add = perf_ibs_add, + .del = perf_ibs_del, + }, + .msr = MSR_AMD64_IBSFETCHCTL, + .config_mask = IBS_FETCH_CONFIG_MASK, + .cnt_mask = IBS_FETCH_MAX_CNT, + .enable_mask = IBS_FETCH_ENABLE, +}; + +static struct perf_ibs perf_ibs_op = { + .pmu = { + .task_ctx_nr = perf_invalid_context, + + .event_init = perf_ibs_init, + .add = perf_ibs_add, + .del = perf_ibs_del, + }, + .msr = MSR_AMD64_IBSOPCTL, + .config_mask = IBS_OP_CONFIG_MASK, + .cnt_mask = IBS_OP_MAX_CNT, + .enable_mask = IBS_OP_ENABLE, }; static __init int perf_event_ibs_init(void) @@ -45,7 +122,8 @@ static __init int perf_event_ibs_init(void) if (!ibs_caps) return -ENODEV; /* ibs not supported by the cpu */ - perf_pmu_register(&perf_ibs, "ibs", -1); + perf_pmu_register(&perf_ibs_fetch.pmu, "ibs_fetch", -1); + perf_pmu_register(&perf_ibs_op.pmu, "ibs_op", -1); printk(KERN_INFO "perf: AMD IBS detected (0x%08x)\n", ibs_caps); return 0; -- cgit v1.2.3 From b7074f1fbd6149eac1ec25063e4a364c39a85473 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 15 Dec 2011 17:56:37 +0100 Subject: perf/x86: Implement IBS interrupt handler This patch implements code to handle ibs interrupts. If ibs data is available a raw perf_event data sample is created and sent back to the userland. This patch only implements the storage of ibs data in the raw sample, but this could be extended in a later patch by generating generic event data such as the rip from the ibs sampling data. Signed-off-by: Robert Richter Acked-by: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1323968199-9326-3-git-send-email-robert.richter@amd.com Signed-off-by: Ingo Molnar --- arch/x86/include/asm/msr-index.h | 5 ++ arch/x86/kernel/cpu/perf_event_amd_ibs.c | 84 ++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) (limited to 'arch') diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index a6962d9161a0..4e3cd382a06f 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -127,6 +127,8 @@ #define MSR_AMD64_IBSFETCHCTL 0xc0011030 #define MSR_AMD64_IBSFETCHLINAD 0xc0011031 #define MSR_AMD64_IBSFETCHPHYSAD 0xc0011032 +#define MSR_AMD64_IBSFETCH_REG_COUNT 3 +#define MSR_AMD64_IBSFETCH_REG_MASK ((1UL< +#include + +#include + #define IBS_FETCH_CONFIG_MASK (IBS_FETCH_RAND_EN | IBS_FETCH_MAX_CNT) #define IBS_OP_CONFIG_MASK IBS_OP_MAX_CNT @@ -25,6 +30,18 @@ struct perf_ibs { u64 config_mask; u64 cnt_mask; u64 enable_mask; + u64 valid_mask; + unsigned long offset_mask[1]; + int offset_max; +}; + +struct perf_ibs_data { + u32 size; + union { + u32 data[0]; /* data buffer starts here */ + u32 caps; + }; + u64 regs[MSR_AMD64_IBS_REG_COUNT_MAX]; }; static struct perf_ibs perf_ibs_fetch; @@ -101,6 +118,9 @@ static struct perf_ibs perf_ibs_fetch = { .config_mask = IBS_FETCH_CONFIG_MASK, .cnt_mask = IBS_FETCH_MAX_CNT, .enable_mask = IBS_FETCH_ENABLE, + .valid_mask = IBS_FETCH_VAL, + .offset_mask = { MSR_AMD64_IBSFETCH_REG_MASK }, + .offset_max = MSR_AMD64_IBSFETCH_REG_COUNT, }; static struct perf_ibs perf_ibs_op = { @@ -115,8 +135,71 @@ static struct perf_ibs perf_ibs_op = { .config_mask = IBS_OP_CONFIG_MASK, .cnt_mask = IBS_OP_MAX_CNT, .enable_mask = IBS_OP_ENABLE, + .valid_mask = IBS_OP_VAL, + .offset_mask = { MSR_AMD64_IBSOP_REG_MASK }, + .offset_max = MSR_AMD64_IBSOP_REG_COUNT, }; +static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs) +{ + struct perf_event *event = NULL; + struct hw_perf_event *hwc = &event->hw; + struct perf_sample_data data; + struct perf_raw_record raw; + struct pt_regs regs; + struct perf_ibs_data ibs_data; + int offset, size; + unsigned int msr; + u64 *buf; + + msr = hwc->config_base; + buf = ibs_data.regs; + rdmsrl(msr, *buf); + if (!(*buf++ & perf_ibs->valid_mask)) + return 0; + + perf_sample_data_init(&data, 0); + if (event->attr.sample_type & PERF_SAMPLE_RAW) { + ibs_data.caps = ibs_caps; + size = 1; + offset = 1; + do { + rdmsrl(msr + offset, *buf++); + size++; + offset = find_next_bit(perf_ibs->offset_mask, + perf_ibs->offset_max, + offset + 1); + } while (offset < perf_ibs->offset_max); + raw.size = sizeof(u32) + sizeof(u64) * size; + raw.data = ibs_data.data; + data.raw = &raw; + } + + regs = *iregs; /* XXX: update ip from ibs sample */ + + if (perf_event_overflow(event, &data, ®s)) + ; /* stop */ + else + /* reenable */ + wrmsrl(hwc->config_base, hwc->config | perf_ibs->enable_mask); + + return 1; +} + +static int __kprobes +perf_ibs_nmi_handler(unsigned int cmd, struct pt_regs *regs) +{ + int handled = 0; + + handled += perf_ibs_handle_irq(&perf_ibs_fetch, regs); + handled += perf_ibs_handle_irq(&perf_ibs_op, regs); + + if (handled) + inc_irq_stat(apic_perf_irqs); + + return handled; +} + static __init int perf_event_ibs_init(void) { if (!ibs_caps) @@ -124,6 +207,7 @@ static __init int perf_event_ibs_init(void) perf_pmu_register(&perf_ibs_fetch.pmu, "ibs_fetch", -1); perf_pmu_register(&perf_ibs_op.pmu, "ibs_op", -1); + register_nmi_handler(NMI_LOCAL, &perf_ibs_nmi_handler, 0, "perf_ibs"); printk(KERN_INFO "perf: AMD IBS detected (0x%08x)\n", ibs_caps); return 0; -- cgit v1.2.3 From 4db2e8e6500d9ba6406f2714fa3968b39a325274 Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 15 Dec 2011 17:56:38 +0100 Subject: perf/x86: Implement IBS pmu control ops Add code to control the IBS pmu. We need to maintain per-cpu states. Since some states are used and changed by the nmi handler, access to these states must be atomic. Signed-off-by: Robert Richter Acked-by: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1323968199-9326-4-git-send-email-robert.richter@amd.com Signed-off-by: Ingo Molnar --- arch/x86/kernel/cpu/perf_event_amd_ibs.c | 106 ++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 3 deletions(-) (limited to 'arch') diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c index a7ec6bdf0a63..40a6d9d5dd23 100644 --- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c +++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c @@ -24,6 +24,19 @@ static u32 ibs_caps; #define IBS_FETCH_CONFIG_MASK (IBS_FETCH_RAND_EN | IBS_FETCH_MAX_CNT) #define IBS_OP_CONFIG_MASK IBS_OP_MAX_CNT +enum ibs_states { + IBS_ENABLED = 0, + IBS_STARTED = 1, + IBS_STOPPING = 2, + + IBS_MAX_STATES, +}; + +struct cpu_perf_ibs { + struct perf_event *event; + unsigned long state[BITS_TO_LONGS(IBS_MAX_STATES)]; +}; + struct perf_ibs { struct pmu pmu; unsigned int msr; @@ -33,6 +46,7 @@ struct perf_ibs { u64 valid_mask; unsigned long offset_mask[1]; int offset_max; + struct cpu_perf_ibs __percpu *pcpu; }; struct perf_ibs_data { @@ -97,15 +111,66 @@ static int perf_ibs_init(struct perf_event *event) return 0; } +static void perf_ibs_start(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); + struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu); + + if (test_and_set_bit(IBS_STARTED, pcpu->state)) + return; + + wrmsrl(hwc->config_base, hwc->config | perf_ibs->enable_mask); +} + +static void perf_ibs_stop(struct perf_event *event, int flags) +{ + struct hw_perf_event *hwc = &event->hw; + struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); + struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu); + u64 val; + + if (!test_and_clear_bit(IBS_STARTED, pcpu->state)) + return; + + set_bit(IBS_STOPPING, pcpu->state); + + rdmsrl(hwc->config_base, val); + val &= ~perf_ibs->enable_mask; + wrmsrl(hwc->config_base, val); +} + static int perf_ibs_add(struct perf_event *event, int flags) { + struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); + struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu); + + if (test_and_set_bit(IBS_ENABLED, pcpu->state)) + return -ENOSPC; + + pcpu->event = event; + + if (flags & PERF_EF_START) + perf_ibs_start(event, PERF_EF_RELOAD); + return 0; } static void perf_ibs_del(struct perf_event *event, int flags) { + struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); + struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu); + + if (!test_and_clear_bit(IBS_ENABLED, pcpu->state)) + return; + + perf_ibs_stop(event, 0); + + pcpu->event = NULL; } +static void perf_ibs_read(struct perf_event *event) { } + static struct perf_ibs perf_ibs_fetch = { .pmu = { .task_ctx_nr = perf_invalid_context, @@ -113,6 +178,9 @@ static struct perf_ibs perf_ibs_fetch = { .event_init = perf_ibs_init, .add = perf_ibs_add, .del = perf_ibs_del, + .start = perf_ibs_start, + .stop = perf_ibs_stop, + .read = perf_ibs_read, }, .msr = MSR_AMD64_IBSFETCHCTL, .config_mask = IBS_FETCH_CONFIG_MASK, @@ -130,6 +198,9 @@ static struct perf_ibs perf_ibs_op = { .event_init = perf_ibs_init, .add = perf_ibs_add, .del = perf_ibs_del, + .start = perf_ibs_start, + .stop = perf_ibs_stop, + .read = perf_ibs_read, }, .msr = MSR_AMD64_IBSOPCTL, .config_mask = IBS_OP_CONFIG_MASK, @@ -142,7 +213,8 @@ static struct perf_ibs perf_ibs_op = { static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs) { - struct perf_event *event = NULL; + struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu); + struct perf_event *event = pcpu->event; struct hw_perf_event *hwc = &event->hw; struct perf_sample_data data; struct perf_raw_record raw; @@ -152,6 +224,14 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs) unsigned int msr; u64 *buf; + if (!test_bit(IBS_STARTED, pcpu->state)) { + /* Catch spurious interrupts after stopping IBS: */ + if (!test_and_clear_bit(IBS_STOPPING, pcpu->state)) + return 0; + rdmsrl(perf_ibs->msr, *ibs_data.regs); + return (*ibs_data.regs & perf_ibs->valid_mask) ? 1 : 0; + } + msr = hwc->config_base; buf = ibs_data.regs; rdmsrl(msr, *buf); @@ -200,13 +280,33 @@ perf_ibs_nmi_handler(unsigned int cmd, struct pt_regs *regs) return handled; } +static __init int perf_ibs_pmu_init(struct perf_ibs *perf_ibs, char *name) +{ + struct cpu_perf_ibs __percpu *pcpu; + int ret; + + pcpu = alloc_percpu(struct cpu_perf_ibs); + if (!pcpu) + return -ENOMEM; + + perf_ibs->pcpu = pcpu; + + ret = perf_pmu_register(&perf_ibs->pmu, name, -1); + if (ret) { + perf_ibs->pcpu = NULL; + free_percpu(pcpu); + } + + return ret; +} + static __init int perf_event_ibs_init(void) { if (!ibs_caps) return -ENODEV; /* ibs not supported by the cpu */ - perf_pmu_register(&perf_ibs_fetch.pmu, "ibs_fetch", -1); - perf_pmu_register(&perf_ibs_op.pmu, "ibs_op", -1); + perf_ibs_pmu_init(&perf_ibs_fetch, "ibs_fetch"); + perf_ibs_pmu_init(&perf_ibs_op, "ibs_op"); register_nmi_handler(NMI_LOCAL, &perf_ibs_nmi_handler, 0, "perf_ibs"); printk(KERN_INFO "perf: AMD IBS detected (0x%08x)\n", ibs_caps); -- cgit v1.2.3 From db98c5faf8cb350212ea3af786cb3ba0d4e7a01e Mon Sep 17 00:00:00 2001 From: Robert Richter Date: Thu, 15 Dec 2011 17:56:39 +0100 Subject: perf/x86: Implement 64-bit counter support for IBS This patch implements 64 bit counter support for IBS. The sampling period is no longer limited to the hw counter width. The functions perf_event_set_period() and perf_event_try_update() can be used as generic functions. They can replace similar code that is duplicate across architectures. Signed-off-by: Robert Richter Acked-by: Peter Zijlstra Cc: Stephane Eranian Link: http://lkml.kernel.org/r/1323968199-9326-5-git-send-email-robert.richter@amd.com Signed-off-by: Ingo Molnar --- arch/x86/include/asm/perf_event.h | 2 + arch/x86/kernel/cpu/perf_event_amd_ibs.c | 204 +++++++++++++++++++++++++++---- 2 files changed, 185 insertions(+), 21 deletions(-) (limited to 'arch') diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index e8fb2c7a5f4f..9cf66965141d 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -177,6 +177,8 @@ struct x86_pmu_capability { #define IBS_FETCH_MAX_CNT 0x0000FFFFULL /* IbsOpCtl bits */ +/* lower 4 bits of the current count are ignored: */ +#define IBS_OP_CUR_CNT (0xFFFF0ULL<<32) #define IBS_OP_CNT_CTL (1ULL<<19) #define IBS_OP_VAL (1ULL<<18) #define IBS_OP_ENABLE (1ULL<<17) diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c index 40a6d9d5dd23..573d24873459 100644 --- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c +++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c @@ -44,9 +44,11 @@ struct perf_ibs { u64 cnt_mask; u64 enable_mask; u64 valid_mask; + u64 max_period; unsigned long offset_mask[1]; int offset_max; struct cpu_perf_ibs __percpu *pcpu; + u64 (*get_count)(u64 config); }; struct perf_ibs_data { @@ -58,6 +60,78 @@ struct perf_ibs_data { u64 regs[MSR_AMD64_IBS_REG_COUNT_MAX]; }; +static int +perf_event_set_period(struct hw_perf_event *hwc, u64 min, u64 max, u64 *count) +{ + s64 left = local64_read(&hwc->period_left); + s64 period = hwc->sample_period; + int overflow = 0; + + /* + * If we are way outside a reasonable range then just skip forward: + */ + if (unlikely(left <= -period)) { + left = period; + local64_set(&hwc->period_left, left); + hwc->last_period = period; + overflow = 1; + } + + if (unlikely(left <= 0)) { + left += period; + local64_set(&hwc->period_left, left); + hwc->last_period = period; + overflow = 1; + } + + if (unlikely(left < min)) + left = min; + + if (left > max) + left = max; + + *count = (u64)left; + + return overflow; +} + +static int +perf_event_try_update(struct perf_event *event, u64 new_raw_count, int width) +{ + struct hw_perf_event *hwc = &event->hw; + int shift = 64 - width; + u64 prev_raw_count; + u64 delta; + + /* + * Careful: an NMI might modify the previous event value. + * + * Our tactic to handle this is to first atomically read and + * exchange a new raw count - then add that new-prev delta + * count to the generic event atomically: + */ + prev_raw_count = local64_read(&hwc->prev_count); + if (local64_cmpxchg(&hwc->prev_count, prev_raw_count, + new_raw_count) != prev_raw_count) + return 0; + + /* + * Now we have the new raw value and have updated the prev + * timestamp already. We can now calculate the elapsed delta + * (event-)time and add that to the generic event. + * + * Careful, not all hw sign-extends above the physical width + * of the count. + */ + delta = (new_raw_count << shift) - (prev_raw_count << shift); + delta >>= shift; + + local64_add(delta, &event->count); + local64_sub(delta, &hwc->period_left); + + return 1; +} + static struct perf_ibs perf_ibs_fetch; static struct perf_ibs perf_ibs_op; @@ -91,18 +165,14 @@ static int perf_ibs_init(struct perf_event *event) if (hwc->sample_period & 0x0f) /* lower 4 bits can not be set in ibs max cnt */ return -EINVAL; - max_cnt = hwc->sample_period >> 4; - if (max_cnt & ~perf_ibs->cnt_mask) - /* out of range */ - return -EINVAL; - config |= max_cnt; } else { max_cnt = config & perf_ibs->cnt_mask; + config &= ~perf_ibs->cnt_mask; event->attr.sample_period = max_cnt << 4; hwc->sample_period = event->attr.sample_period; } - if (!max_cnt) + if (!hwc->sample_period) return -EINVAL; hwc->config_base = perf_ibs->msr; @@ -111,16 +181,71 @@ static int perf_ibs_init(struct perf_event *event) return 0; } +static int perf_ibs_set_period(struct perf_ibs *perf_ibs, + struct hw_perf_event *hwc, u64 *period) +{ + int ret; + + /* ignore lower 4 bits in min count: */ + ret = perf_event_set_period(hwc, 1<<4, perf_ibs->max_period, period); + local64_set(&hwc->prev_count, 0); + + return ret; +} + +static u64 get_ibs_fetch_count(u64 config) +{ + return (config & IBS_FETCH_CNT) >> 12; +} + +static u64 get_ibs_op_count(u64 config) +{ + return (config & IBS_OP_CUR_CNT) >> 32; +} + +static void +perf_ibs_event_update(struct perf_ibs *perf_ibs, struct perf_event *event, + u64 config) +{ + u64 count = perf_ibs->get_count(config); + + while (!perf_event_try_update(event, count, 20)) { + rdmsrl(event->hw.config_base, config); + count = perf_ibs->get_count(config); + } +} + +/* Note: The enable mask must be encoded in the config argument. */ +static inline void perf_ibs_enable_event(struct hw_perf_event *hwc, u64 config) +{ + wrmsrl(hwc->config_base, hwc->config | config); +} + +/* + * We cannot restore the ibs pmu state, so we always needs to update + * the event while stopping it and then reset the state when starting + * again. Thus, ignoring PERF_EF_RELOAD and PERF_EF_UPDATE flags in + * perf_ibs_start()/perf_ibs_stop() and instead always do it. + */ static void perf_ibs_start(struct perf_event *event, int flags) { struct hw_perf_event *hwc = &event->hw; struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu); + u64 config; - if (test_and_set_bit(IBS_STARTED, pcpu->state)) + if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED))) return; - wrmsrl(hwc->config_base, hwc->config | perf_ibs->enable_mask); + WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE)); + hwc->state = 0; + + perf_ibs_set_period(perf_ibs, hwc, &config); + config = (config >> 4) | perf_ibs->enable_mask; + set_bit(IBS_STARTED, pcpu->state); + perf_ibs_enable_event(hwc, config); + + perf_event_update_userpage(event); } static void perf_ibs_stop(struct perf_event *event, int flags) @@ -129,15 +254,28 @@ static void perf_ibs_stop(struct perf_event *event, int flags) struct perf_ibs *perf_ibs = container_of(event->pmu, struct perf_ibs, pmu); struct cpu_perf_ibs *pcpu = this_cpu_ptr(perf_ibs->pcpu); u64 val; + int stopping; - if (!test_and_clear_bit(IBS_STARTED, pcpu->state)) - return; + stopping = test_and_clear_bit(IBS_STARTED, pcpu->state); - set_bit(IBS_STOPPING, pcpu->state); + if (!stopping && (hwc->state & PERF_HES_UPTODATE)) + return; rdmsrl(hwc->config_base, val); - val &= ~perf_ibs->enable_mask; - wrmsrl(hwc->config_base, val); + + if (stopping) { + set_bit(IBS_STOPPING, pcpu->state); + val &= ~perf_ibs->enable_mask; + wrmsrl(hwc->config_base, val); + WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED); + hwc->state |= PERF_HES_STOPPED; + } + + if (hwc->state & PERF_HES_UPTODATE) + return; + + perf_ibs_event_update(perf_ibs, event, val); + hwc->state |= PERF_HES_UPTODATE; } static int perf_ibs_add(struct perf_event *event, int flags) @@ -148,6 +286,8 @@ static int perf_ibs_add(struct perf_event *event, int flags) if (test_and_set_bit(IBS_ENABLED, pcpu->state)) return -ENOSPC; + event->hw.state = PERF_HES_UPTODATE | PERF_HES_STOPPED; + pcpu->event = event; if (flags & PERF_EF_START) @@ -164,9 +304,11 @@ static void perf_ibs_del(struct perf_event *event, int flags) if (!test_and_clear_bit(IBS_ENABLED, pcpu->state)) return; - perf_ibs_stop(event, 0); + perf_ibs_stop(event, PERF_EF_UPDATE); pcpu->event = NULL; + + perf_event_update_userpage(event); } static void perf_ibs_read(struct perf_event *event) { } @@ -187,8 +329,11 @@ static struct perf_ibs perf_ibs_fetch = { .cnt_mask = IBS_FETCH_MAX_CNT, .enable_mask = IBS_FETCH_ENABLE, .valid_mask = IBS_FETCH_VAL, + .max_period = IBS_FETCH_MAX_CNT << 4, .offset_mask = { MSR_AMD64_IBSFETCH_REG_MASK }, .offset_max = MSR_AMD64_IBSFETCH_REG_COUNT, + + .get_count = get_ibs_fetch_count, }; static struct perf_ibs perf_ibs_op = { @@ -207,8 +352,11 @@ static struct perf_ibs perf_ibs_op = { .cnt_mask = IBS_OP_MAX_CNT, .enable_mask = IBS_OP_ENABLE, .valid_mask = IBS_OP_VAL, + .max_period = IBS_OP_MAX_CNT << 4, .offset_mask = { MSR_AMD64_IBSOP_REG_MASK }, .offset_max = MSR_AMD64_IBSOP_REG_COUNT, + + .get_count = get_ibs_op_count, }; static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs) @@ -220,9 +368,9 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs) struct perf_raw_record raw; struct pt_regs regs; struct perf_ibs_data ibs_data; - int offset, size; + int offset, size, overflow, reenable; unsigned int msr; - u64 *buf; + u64 *buf, config; if (!test_bit(IBS_STARTED, pcpu->state)) { /* Catch spurious interrupts after stopping IBS: */ @@ -257,11 +405,25 @@ static int perf_ibs_handle_irq(struct perf_ibs *perf_ibs, struct pt_regs *iregs) regs = *iregs; /* XXX: update ip from ibs sample */ - if (perf_event_overflow(event, &data, ®s)) - ; /* stop */ - else - /* reenable */ - wrmsrl(hwc->config_base, hwc->config | perf_ibs->enable_mask); + /* + * Emulate IbsOpCurCnt in MSRC001_1033 (IbsOpCtl), not + * supported in all cpus. As this triggered an interrupt, we + * set the current count to the max count. + */ + config = ibs_data.regs[0]; + if (perf_ibs == &perf_ibs_op && !(ibs_caps & IBS_CAPS_RDWROPCNT)) { + config &= ~IBS_OP_CUR_CNT; + config |= (config & IBS_OP_MAX_CNT) << 36; + } + + perf_ibs_event_update(perf_ibs, event, config); + + overflow = perf_ibs_set_period(perf_ibs, hwc, &config); + reenable = !(overflow && perf_event_overflow(event, &data, ®s)); + config = (config >> 4) | (reenable ? perf_ibs->enable_mask : 0); + perf_ibs_enable_event(hwc, config); + + perf_event_update_userpage(event); return 1; } -- cgit v1.2.3 From 93651b85bbd1479321af1de0d14ca95c231e538a Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Fri, 23 Sep 2011 13:51:57 +0200 Subject: ARM: OMAP2+: board-generic: Remove i2c static init This mainly reverts the commit that was adding the i2c static init. Since the i2c and twl nodes are now present, there is no need for the static initialization anymore. Signed-off-by: Benoit Cousson Cc: Tony Lindgren Acked-by: Rob Herring Acked-by: Grant Likely --- arch/arm/mach-omap2/board-generic.c | 37 ++----------------------------------- 1 file changed, 2 insertions(+), 35 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-omap2/board-generic.c b/arch/arm/mach-omap2/board-generic.c index 74e1687b5170..7302ba7ff1b9 100644 --- a/arch/arm/mach-omap2/board-generic.c +++ b/arch/arm/mach-omap2/board-generic.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include @@ -95,22 +94,6 @@ MACHINE_END #endif #ifdef CONFIG_ARCH_OMAP3 -static struct twl4030_platform_data beagle_twldata = { - .irq_base = TWL4030_IRQ_BASE, - .irq_end = TWL4030_IRQ_END, -}; - -static void __init omap3_i2c_init(void) -{ - omap3_pmic_init("twl4030", &beagle_twldata); -} - -static void __init omap3_init(void) -{ - omap3_i2c_init(); - omap_generic_init(); -} - static const char *omap3_boards_compat[] __initdata = { "ti,omap3", NULL, @@ -122,7 +105,7 @@ DT_MACHINE_START(OMAP3_DT, "Generic OMAP3 (Flattened Device Tree)") .init_early = omap3430_init_early, .init_irq = omap_init_irq, .handle_irq = omap3_intc_handle_irq, - .init_machine = omap3_init, + .init_machine = omap_generic_init, .timer = &omap3_timer, .dt_compat = omap3_boards_compat, .restart = omap_prcm_restart, @@ -130,22 +113,6 @@ MACHINE_END #endif #ifdef CONFIG_ARCH_OMAP4 -static struct twl4030_platform_data sdp4430_twldata = { - .irq_base = TWL6030_IRQ_BASE, - .irq_end = TWL6030_IRQ_END, -}; - -static void __init omap4_i2c_init(void) -{ - omap4_pmic_init("twl6030", &sdp4430_twldata); -} - -static void __init omap4_init(void) -{ - omap4_i2c_init(); - omap_generic_init(); -} - static const char *omap4_boards_compat[] __initdata = { "ti,omap4", NULL, @@ -157,7 +124,7 @@ DT_MACHINE_START(OMAP4_DT, "Generic OMAP4 (Flattened Device Tree)") .init_early = omap4430_init_early, .init_irq = omap_init_irq, .handle_irq = gic_handle_irq, - .init_machine = omap4_init, + .init_machine = omap_generic_init, .timer = &omap4_timer, .dt_compat = omap4_boards_compat, .restart = omap_prcm_restart, -- cgit v1.2.3 From e3e5a92db430689fe918041e99cb04b8761f5a50 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Tue, 16 Aug 2011 11:51:54 +0200 Subject: arm/dts: OMAP4: Add gpio nodes Add the 6 GPIOs controller nodes present in OMAP4. Remove gpio static device initialisation if DT is populated. Signed-off-by: Benoit Cousson Cc: Grant Likely --- arch/arm/boot/dts/omap4.dtsi | 54 ++++++++++++++++++++++++++++++++++++++++++++ arch/arm/mach-omap2/gpio.c | 8 +++++-- 2 files changed, 60 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi index 3d35559e77bc..7f8463b666fd 100644 --- a/arch/arm/boot/dts/omap4.dtsi +++ b/arch/arm/boot/dts/omap4.dtsi @@ -104,6 +104,60 @@ <0x48240100 0x0100>; }; + gpio1: gpio@4a310000 { + compatible = "ti,omap4-gpio"; + ti,hwmods = "gpio1"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <1>; + }; + + gpio2: gpio@48055000 { + compatible = "ti,omap4-gpio"; + ti,hwmods = "gpio2"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <1>; + }; + + gpio3: gpio@48057000 { + compatible = "ti,omap4-gpio"; + ti,hwmods = "gpio3"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <1>; + }; + + gpio4: gpio@48059000 { + compatible = "ti,omap4-gpio"; + ti,hwmods = "gpio4"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <1>; + }; + + gpio5: gpio@4805b000 { + compatible = "ti,omap4-gpio"; + ti,hwmods = "gpio5"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <1>; + }; + + gpio6: gpio@4805d000 { + compatible = "ti,omap4-gpio"; + ti,hwmods = "gpio6"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <1>; + }; + uart1: serial@4806a000 { compatible = "ti,omap4-uart"; ti,hwmods = "uart1"; diff --git a/arch/arm/mach-omap2/gpio.c b/arch/arm/mach-omap2/gpio.c index 8cbfbc2918ce..8412746bad71 100644 --- a/arch/arm/mach-omap2/gpio.c +++ b/arch/arm/mach-omap2/gpio.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -122,7 +123,10 @@ static int omap2_gpio_dev_init(struct omap_hwmod *oh, void *unused) */ static int __init omap2_gpio_init(void) { - return omap_hwmod_for_each_by_class("gpio", omap2_gpio_dev_init, - NULL); + /* If dtb is there, the devices will be created dynamically */ + if (of_have_populated_dt()) + return -ENODEV; + + return omap_hwmod_for_each_by_class("gpio", omap2_gpio_dev_init, NULL); } postcore_initcall(omap2_gpio_init); -- cgit v1.2.3 From 385a64bbc2d2c3ef864190b010c74afc5b850195 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Tue, 16 Aug 2011 11:51:54 +0200 Subject: arm/dts: OMAP3: Add gpio nodes Add the 6 GPIOs controller nodes present in OMAP3. Signed-off-by: Benoit Cousson Cc: Grant Likely --- arch/arm/boot/dts/omap3.dtsi | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) (limited to 'arch') diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi index c6121357c1eb..4615ebb6cb93 100644 --- a/arch/arm/boot/dts/omap3.dtsi +++ b/arch/arm/boot/dts/omap3.dtsi @@ -69,6 +69,60 @@ reg = <0x48200000 0x1000>; }; + gpio1: gpio@48310000 { + compatible = "ti,omap3-gpio"; + ti,hwmods = "gpio1"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <1>; + }; + + gpio2: gpio@49050000 { + compatible = "ti,omap3-gpio"; + ti,hwmods = "gpio2"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <1>; + }; + + gpio3: gpio@49052000 { + compatible = "ti,omap3-gpio"; + ti,hwmods = "gpio3"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <1>; + }; + + gpio4: gpio@49054000 { + compatible = "ti,omap3-gpio"; + ti,hwmods = "gpio4"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <1>; + }; + + gpio5: gpio@49056000 { + compatible = "ti,omap3-gpio"; + ti,hwmods = "gpio5"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <1>; + }; + + gpio6: gpio@49058000 { + compatible = "ti,omap3-gpio"; + ti,hwmods = "gpio6"; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <1>; + }; + uart1: serial@4806a000 { compatible = "ti,omap3-uart"; ti,hwmods = "uart1"; -- cgit v1.2.3 From efcf1e502012cc2cc9ee8165fa0a7679725c525c Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Fri, 20 Jan 2012 14:15:58 +0100 Subject: arm/dts: OMAP4: Add SPI controller nodes Add the 4 McSPI controller nodes present in an OMAP4 device. Remove SPI static device initialisation if DT is populated. Signed-off-by: Benoit Cousson Cc: Grant Likely Acked-by: Rob Herring --- arch/arm/boot/dts/omap4.dtsi | 32 ++++++++++++++++++++++++++++++++ arch/arm/mach-omap2/devices.c | 4 +++- 2 files changed, 35 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi index 7f8463b666fd..6c584534c290 100644 --- a/arch/arm/boot/dts/omap4.dtsi +++ b/arch/arm/boot/dts/omap4.dtsi @@ -209,5 +209,37 @@ #size-cells = <0>; ti,hwmods = "i2c4"; }; + + mcspi1: spi@48098000 { + compatible = "ti,omap4-mcspi"; + #address-cells = <1>; + #size-cells = <0>; + ti,hwmods = "mcspi1"; + ti,spi-num-cs = <4>; + }; + + mcspi2: spi@4809a000 { + compatible = "ti,omap4-mcspi"; + #address-cells = <1>; + #size-cells = <0>; + ti,hwmods = "mcspi2"; + ti,spi-num-cs = <2>; + }; + + mcspi3: spi@480b8000 { + compatible = "ti,omap4-mcspi"; + #address-cells = <1>; + #size-cells = <0>; + ti,hwmods = "mcspi3"; + ti,spi-num-cs = <2>; + }; + + mcspi4: spi@480ba000 { + compatible = "ti,omap4-mcspi"; + #address-cells = <1>; + #size-cells = <0>; + ti,hwmods = "mcspi4"; + ti,spi-num-cs = <1>; + }; }; }; diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index 283d11eae693..8a489ba68714 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -725,7 +725,9 @@ static int __init omap2_init_devices(void) omap_init_dmic(); omap_init_camera(); omap_init_mbox(); - omap_init_mcspi(); + /* If dtb is there, the devices will be created dynamically */ + if (!of_have_populated_dt()) + omap_init_mcspi(); omap_init_pmu(); omap_hdq_init(); omap_init_sti(); -- cgit v1.2.3 From fc72d248d01a3bfef4cde6fe642536c94bee1306 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Fri, 20 Jan 2012 14:15:58 +0100 Subject: arm/dts: OMAP3: Add SPI controller nodes Add the 4 McSPI controller nodes present in an OMAP3 device. Signed-off-by: Benoit Cousson Cc: Grant Likely Acked-by: Rob Herring --- arch/arm/boot/dts/omap3.dtsi | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'arch') diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi index 4615ebb6cb93..dc9425c48ed9 100644 --- a/arch/arm/boot/dts/omap3.dtsi +++ b/arch/arm/boot/dts/omap3.dtsi @@ -167,5 +167,37 @@ #size-cells = <0>; ti,hwmods = "i2c3"; }; + + mcspi1: spi@48098000 { + compatible = "ti,omap2-mcspi"; + #address-cells = <1>; + #size-cells = <0>; + ti,hwmods = "mcspi1"; + ti,spi-num-cs = <4>; + }; + + mcspi2: spi@4809a000 { + compatible = "ti,omap2-mcspi"; + #address-cells = <1>; + #size-cells = <0>; + ti,hwmods = "mcspi2"; + ti,spi-num-cs = <2>; + }; + + mcspi3: spi@480b8000 { + compatible = "ti,omap2-mcspi"; + #address-cells = <1>; + #size-cells = <0>; + ti,hwmods = "mcspi3"; + ti,spi-num-cs = <2>; + }; + + mcspi4: spi@480ba000 { + compatible = "ti,omap2-mcspi"; + #address-cells = <1>; + #size-cells = <0>; + ti,hwmods = "mcspi4"; + ti,spi-num-cs = <1>; + }; }; }; -- cgit v1.2.3 From e7c64db9f50d6523e20bc5b8f042645612e37b74 Mon Sep 17 00:00:00 2001 From: Benoit Cousson Date: Fri, 20 Jan 2012 14:17:00 +0100 Subject: arm/dts: omap4-sdp: Add ks8851 ethernet SPI device Add an ethernet SPI chip in the OMAP4 SDP/Blaze board DTS file. Add a fixed regulator node controlled by a GPIO line to supply the ethernet chip. Based on original code from Rajendra. Signed-off-by: Benoit Cousson Signed-off-by: Rajendra Nayak Acked-by: Rob Herring --- arch/arm/boot/dts/omap4-sdp.dts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'arch') diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts index 01db8b78ea71..31938c1c3f14 100644 --- a/arch/arm/boot/dts/omap4-sdp.dts +++ b/arch/arm/boot/dts/omap4-sdp.dts @@ -17,6 +17,16 @@ device_type = "memory"; reg = <0x80000000 0x40000000>; /* 1 GB */ }; + + vdd_eth: fixedregulator@0 { + compatible = "regulator-fixed"; + regulator-name = "VDD_ETH"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + gpio = <&gpio2 16 0>; /* gpio line 48 */ + enable-active-high; + regulator-boot-on; + }; }; &i2c1 { @@ -70,3 +80,14 @@ reg = <0x1e>; }; }; + +&mcspi1 { + eth@0 { + compatible = "ks8851"; + spi-max-frequency = <24000000>; + reg = <0>; + interrupt-parent = <&gpio2>; + interrupts = <2>; /* gpio line 34 */ + vdd-supply = <&vdd_eth>; + }; +}; -- cgit v1.2.3 From ecc7b060a76d2b454be79abe2c6b934dee850413 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Wed, 8 Feb 2012 15:50:12 -0800 Subject: arm/dts: twl: Pass regulator data from dt Pass all the voltage regulator information for twl6030/twl4030 PMIC from device tree. Signed-off-by: Rajendra Nayak Signed-off-by: Benoit Cousson --- arch/arm/boot/dts/omap3-beagle.dts | 6 ++++ arch/arm/boot/dts/twl4030.dtsi | 18 +++++++++++ arch/arm/boot/dts/twl6030.dtsi | 64 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) (limited to 'arch') diff --git a/arch/arm/boot/dts/omap3-beagle.dts b/arch/arm/boot/dts/omap3-beagle.dts index 714ba5d506ce..54556b19c97d 100644 --- a/arch/arm/boot/dts/omap3-beagle.dts +++ b/arch/arm/boot/dts/omap3-beagle.dts @@ -26,6 +26,12 @@ reg = <0x48>; interrupts = <7>; /* SYS_NIRQ cascaded to intc */ interrupt-parent = <&intc>; + + vsim: regulator@10 { + compatible = "ti,twl4030-vsim"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3000000>; + }; }; }; diff --git a/arch/arm/boot/dts/twl4030.dtsi b/arch/arm/boot/dts/twl4030.dtsi index 8be5223e40eb..a94654c9eb8d 100644 --- a/arch/arm/boot/dts/twl4030.dtsi +++ b/arch/arm/boot/dts/twl4030.dtsi @@ -18,4 +18,22 @@ compatible = "ti,twl4030-rtc"; interrupts = <11>; }; + + vdac: regulator@0 { + compatible = "ti,twl4030-vdac"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + vpll2: regulator@1 { + compatible = "ti,twl4030-vpll2"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + }; + + vmmc1: regulator@2 { + compatible = "ti,twl4030-vmmc1"; + regulator-min-microvolt = <1850000>; + regulator-max-microvolt = <3150000>; + }; }; diff --git a/arch/arm/boot/dts/twl6030.dtsi b/arch/arm/boot/dts/twl6030.dtsi index b7b4e5e4340a..3b2f3510d7eb 100644 --- a/arch/arm/boot/dts/twl6030.dtsi +++ b/arch/arm/boot/dts/twl6030.dtsi @@ -19,4 +19,68 @@ compatible = "ti,twl4030-rtc"; interrupts = <11>; }; + + vaux1: regulator@0 { + compatible = "ti,twl6030-vaux1"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + }; + + vaux2: regulator@1 { + compatible = "ti,twl6030-vaux2"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <2800000>; + }; + + vaux3: regulator@2 { + compatible = "ti,twl6030-vaux3"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + }; + + vmmc: regulator@3 { + compatible = "ti,twl6030-vmmc"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3000000>; + }; + + vpp: regulator@4 { + compatible = "ti,twl6030-vpp"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2500000>; + }; + + vusim: regulator@5 { + compatible = "ti,twl6030-vusim"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <2900000>; + }; + + vdac: regulator@6 { + compatible = "ti,twl6030-vdac"; + }; + + vana: regulator@7 { + compatible = "ti,twl6030-vana"; + }; + + vcxio: regulator@8 { + compatible = "ti,twl6030-vcxio"; + }; + + vusb: regulator@9 { + compatible = "ti,twl6030-vusb"; + }; + + v1v8: regulator@10 { + compatible = "ti,twl6030-v1v8"; + }; + + v2v1: regulator@11 { + compatible = "ti,twl6030-v2v1"; + }; + + clk32kg: regulator@12 { + compatible = "ti,twl6030-clk32kg"; + }; }; -- cgit v1.2.3 From 900771a483ef28915a48066d7895d8252315607a Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Mon, 12 Mar 2012 14:55:14 +0530 Subject: uprobes/core: Make macro names consistent Rename macros that refer to individual uprobe to start with UPROBE_ instead of UPROBES_. This is pure cleanup, no functional change intended. Signed-off-by: Srikar Dronamraju Cc: Linus Torvalds Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Linux-mm Cc: Oleg Nesterov Cc: Andi Kleen Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Arnaldo Carvalho de Melo Cc: Masami Hiramatsu Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120312092514.5379.36595.sendpatchset@srdronam.in.ibm.com Signed-off-by: Ingo Molnar --- arch/x86/include/asm/uprobes.h | 6 +++--- arch/x86/kernel/uprobes.c | 18 +++++++++--------- include/linux/uprobes.h | 4 ++-- kernel/events/uprobes.c | 18 +++++++++--------- 4 files changed, 23 insertions(+), 23 deletions(-) (limited to 'arch') diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index f7ce310a429d..5c399e446512 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h @@ -26,10 +26,10 @@ typedef u8 uprobe_opcode_t; #define MAX_UINSN_BYTES 16 -#define UPROBES_XOL_SLOT_BYTES 128 /* to keep it cache aligned */ +#define UPROBE_XOL_SLOT_BYTES 128 /* to keep it cache aligned */ -#define UPROBES_BKPT_INSN 0xcc -#define UPROBES_BKPT_INSN_SIZE 1 +#define UPROBE_BKPT_INSN 0xcc +#define UPROBE_BKPT_INSN_SIZE 1 struct arch_uprobe { u16 fixups; diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 04dfcef2d028..6dfa89e6f24a 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -31,14 +31,14 @@ /* Post-execution fixups. */ /* No fixup needed */ -#define UPROBES_FIX_NONE 0x0 +#define UPROBE_FIX_NONE 0x0 /* Adjust IP back to vicinity of actual insn */ -#define UPROBES_FIX_IP 0x1 +#define UPROBE_FIX_IP 0x1 /* Adjust the return address of a call insn */ -#define UPROBES_FIX_CALL 0x2 +#define UPROBE_FIX_CALL 0x2 -#define UPROBES_FIX_RIP_AX 0x8000 -#define UPROBES_FIX_RIP_CX 0x4000 +#define UPROBE_FIX_RIP_AX 0x8000 +#define UPROBE_FIX_RIP_CX 0x4000 /* Adaptations for mhiramat x86 decoder v14. */ #define OPCODE1(insn) ((insn)->opcode.bytes[0]) @@ -269,9 +269,9 @@ static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn) break; } if (fix_ip) - auprobe->fixups |= UPROBES_FIX_IP; + auprobe->fixups |= UPROBE_FIX_IP; if (fix_call) - auprobe->fixups |= UPROBES_FIX_CALL; + auprobe->fixups |= UPROBE_FIX_CALL; } #ifdef CONFIG_X86_64 @@ -341,12 +341,12 @@ static void handle_riprel_insn(struct mm_struct *mm, struct arch_uprobe *auprobe * is NOT the register operand, so we use %rcx (register * #1) for the scratch register. */ - auprobe->fixups = UPROBES_FIX_RIP_CX; + auprobe->fixups = UPROBE_FIX_RIP_CX; /* Change modrm from 00 000 101 to 00 000 001. */ *cursor = 0x1; } else { /* Use %rax (register #0) for the scratch register. */ - auprobe->fixups = UPROBES_FIX_RIP_AX; + auprobe->fixups = UPROBE_FIX_RIP_AX; /* Change modrm from 00 xxx 101 to 00 xxx 000 */ *cursor = (reg << 3); } diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index f85797e1ccd4..838fb312926a 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -35,10 +35,10 @@ struct vm_area_struct; /* flags that denote/change uprobes behaviour */ /* Have a copy of original instruction */ -#define UPROBES_COPY_INSN 0x1 +#define UPROBE_COPY_INSN 0x1 /* Dont run handlers when first register/ last unregister in progress*/ -#define UPROBES_RUN_HANDLER 0x2 +#define UPROBE_RUN_HANDLER 0x2 struct uprobe_consumer { int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs); diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 5ce32e3ae9e9..0d36bf3920ba 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -177,7 +177,7 @@ out: */ bool __weak is_bkpt_insn(uprobe_opcode_t *insn) { - return *insn == UPROBES_BKPT_INSN; + return *insn == UPROBE_BKPT_INSN; } /* @@ -259,8 +259,8 @@ static int write_opcode(struct mm_struct *mm, struct arch_uprobe *auprobe, /* poke the new insn in, ASSUMES we don't cross page boundary */ vaddr &= ~PAGE_MASK; - BUG_ON(vaddr + UPROBES_BKPT_INSN_SIZE > PAGE_SIZE); - memcpy(vaddr_new + vaddr, &opcode, UPROBES_BKPT_INSN_SIZE); + BUG_ON(vaddr + UPROBE_BKPT_INSN_SIZE > PAGE_SIZE); + memcpy(vaddr_new + vaddr, &opcode, UPROBE_BKPT_INSN_SIZE); kunmap_atomic(vaddr_new); kunmap_atomic(vaddr_old); @@ -308,7 +308,7 @@ static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_ lock_page(page); vaddr_new = kmap_atomic(page); vaddr &= ~PAGE_MASK; - memcpy(opcode, vaddr_new + vaddr, UPROBES_BKPT_INSN_SIZE); + memcpy(opcode, vaddr_new + vaddr, UPROBE_BKPT_INSN_SIZE); kunmap_atomic(vaddr_new); unlock_page(page); @@ -352,7 +352,7 @@ int __weak set_bkpt(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned if (result) return result; - return write_opcode(mm, auprobe, vaddr, UPROBES_BKPT_INSN); + return write_opcode(mm, auprobe, vaddr, UPROBE_BKPT_INSN); } /** @@ -635,7 +635,7 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, addr = (unsigned long)vaddr; - if (!(uprobe->flags & UPROBES_COPY_INSN)) { + if (!(uprobe->flags & UPROBE_COPY_INSN)) { ret = copy_insn(uprobe, vma, addr); if (ret) return ret; @@ -647,7 +647,7 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, if (ret) return ret; - uprobe->flags |= UPROBES_COPY_INSN; + uprobe->flags |= UPROBE_COPY_INSN; } ret = set_bkpt(mm, &uprobe->arch, addr); @@ -857,7 +857,7 @@ int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer * uprobe->consumers = NULL; __uprobe_unregister(uprobe); } else { - uprobe->flags |= UPROBES_RUN_HANDLER; + uprobe->flags |= UPROBE_RUN_HANDLER; } } @@ -889,7 +889,7 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consume if (consumer_del(uprobe, consumer)) { if (!uprobe->consumers) { __uprobe_unregister(uprobe); - uprobe->flags &= ~UPROBES_RUN_HANDLER; + uprobe->flags &= ~UPROBE_RUN_HANDLER; } } -- cgit v1.2.3 From e3343e6a2819ff5d0dfc4bb5c9fb7f9a4d04da73 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Mon, 12 Mar 2012 14:55:30 +0530 Subject: uprobes/core: Make order of function parameters consistent across functions If a function takes struct uprobe or struct arch_uprobe, then it is passed as the first parameter. This is pure cleanup, no functional change intended. Signed-off-by: Srikar Dronamraju Cc: Linus Torvalds Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Linux-mm Cc: Oleg Nesterov Cc: Andi Kleen Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Arnaldo Carvalho de Melo Cc: Masami Hiramatsu Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120312092530.5379.18394.sendpatchset@srdronam.in.ibm.com Signed-off-by: Ingo Molnar --- arch/x86/include/asm/uprobes.h | 2 +- arch/x86/kernel/uprobes.c | 15 +++---- include/linux/uprobes.h | 12 +++--- kernel/events/uprobes.c | 93 ++++++++++++++++++++++-------------------- 4 files changed, 63 insertions(+), 59 deletions(-) (limited to 'arch') diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index 5c399e446512..384f1bebf884 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h @@ -39,5 +39,5 @@ struct arch_uprobe { #endif }; -extern int arch_uprobes_analyze_insn(struct mm_struct *mm, struct arch_uprobe *arch_uprobe); +extern int arch_uprobes_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm); #endif /* _ASM_UPROBES_H */ diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 6dfa89e6f24a..851a11b0d38c 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -297,7 +297,8 @@ static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn) * - There's never a SIB byte. * - The displacement is always 4 bytes. */ -static void handle_riprel_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) +static void +handle_riprel_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) { u8 *cursor; u8 reg; @@ -381,19 +382,19 @@ static int validate_insn_64bits(struct arch_uprobe *auprobe, struct insn *insn) return -ENOTSUPP; } -static int validate_insn_bits(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) +static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) { if (mm->context.ia32_compat) return validate_insn_32bits(auprobe, insn); return validate_insn_64bits(auprobe, insn); } #else /* 32-bit: */ -static void handle_riprel_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) +static void handle_riprel_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) { /* No RIP-relative addressing on 32-bit */ } -static int validate_insn_bits(struct mm_struct *mm, struct arch_uprobe *auprobe, struct insn *insn) +static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm, struct insn *insn) { return validate_insn_32bits(auprobe, insn); } @@ -405,17 +406,17 @@ static int validate_insn_bits(struct mm_struct *mm, struct arch_uprobe *auprobe, * @arch_uprobe: the probepoint information. * Return 0 on success or a -ve number on error. */ -int arch_uprobes_analyze_insn(struct mm_struct *mm, struct arch_uprobe *auprobe) +int arch_uprobes_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm) { int ret; struct insn insn; auprobe->fixups = 0; - ret = validate_insn_bits(mm, auprobe, &insn); + ret = validate_insn_bits(auprobe, mm, &insn); if (ret != 0) return ret; - handle_riprel_insn(mm, auprobe, &insn); + handle_riprel_insn(auprobe, mm, &insn); prepare_fixups(auprobe, &insn); return 0; diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 838fb312926a..58699182e9a7 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -52,20 +52,20 @@ struct uprobe_consumer { }; #ifdef CONFIG_UPROBES -extern int __weak set_bkpt(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr); -extern int __weak set_orig_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr, bool verify); +extern int __weak set_bkpt(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); +extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr, bool verify); extern bool __weak is_bkpt_insn(uprobe_opcode_t *insn); -extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer); -extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer); +extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); +extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_mmap(struct vm_area_struct *vma); #else /* CONFIG_UPROBES is not defined */ static inline int -uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) +uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) { return -ENOSYS; } static inline void -uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) +uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) { } static inline int uprobe_mmap(struct vm_area_struct *vma) diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 0d36bf3920ba..9c5ddff1c8da 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -192,8 +192,8 @@ bool __weak is_bkpt_insn(uprobe_opcode_t *insn) /* * write_opcode - write the opcode at a given virtual address. + * @auprobe: arch breakpointing information. * @mm: the probed process address space. - * @arch_uprobe: the breakpointing information. * @vaddr: the virtual address to store the opcode. * @opcode: opcode to be written at @vaddr. * @@ -203,7 +203,7 @@ bool __weak is_bkpt_insn(uprobe_opcode_t *insn) * For mm @mm, write the opcode at @vaddr. * Return 0 (success) or a negative errno. */ -static int write_opcode(struct mm_struct *mm, struct arch_uprobe *auprobe, +static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_t opcode) { struct page *old_page, *new_page; @@ -334,14 +334,14 @@ static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) /** * set_bkpt - store breakpoint at a given address. + * @auprobe: arch specific probepoint information. * @mm: the probed process address space. - * @uprobe: the probepoint information. * @vaddr: the virtual address to insert the opcode. * * For mm @mm, store the breakpoint instruction at @vaddr. * Return 0 (success) or a negative errno. */ -int __weak set_bkpt(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr) +int __weak set_bkpt(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) { int result; @@ -352,13 +352,13 @@ int __weak set_bkpt(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned if (result) return result; - return write_opcode(mm, auprobe, vaddr, UPROBE_BKPT_INSN); + return write_opcode(auprobe, mm, vaddr, UPROBE_BKPT_INSN); } /** * set_orig_insn - Restore the original instruction. * @mm: the probed process address space. - * @uprobe: the probepoint information. + * @auprobe: arch specific probepoint information. * @vaddr: the virtual address to insert the opcode. * @verify: if true, verify existance of breakpoint instruction. * @@ -366,7 +366,7 @@ int __weak set_bkpt(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned * Return 0 (success) or a negative errno. */ int __weak -set_orig_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long vaddr, bool verify) +set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr, bool verify) { if (verify) { int result; @@ -378,7 +378,7 @@ set_orig_insn(struct mm_struct *mm, struct arch_uprobe *auprobe, unsigned long v if (result != 1) return result; } - return write_opcode(mm, auprobe, vaddr, *(uprobe_opcode_t *)auprobe->insn); + return write_opcode(auprobe, mm, vaddr, *(uprobe_opcode_t *)auprobe->insn); } static int match_uprobe(struct uprobe *l, struct uprobe *r) @@ -525,30 +525,30 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) /* Returns the previous consumer */ static struct uprobe_consumer * -consumer_add(struct uprobe *uprobe, struct uprobe_consumer *consumer) +consumer_add(struct uprobe *uprobe, struct uprobe_consumer *uc) { down_write(&uprobe->consumer_rwsem); - consumer->next = uprobe->consumers; - uprobe->consumers = consumer; + uc->next = uprobe->consumers; + uprobe->consumers = uc; up_write(&uprobe->consumer_rwsem); - return consumer->next; + return uc->next; } /* - * For uprobe @uprobe, delete the consumer @consumer. - * Return true if the @consumer is deleted successfully + * For uprobe @uprobe, delete the consumer @uc. + * Return true if the @uc is deleted successfully * or return false. */ -static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *consumer) +static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *uc) { struct uprobe_consumer **con; bool ret = false; down_write(&uprobe->consumer_rwsem); for (con = &uprobe->consumers; *con; con = &(*con)->next) { - if (*con == consumer) { - *con = consumer->next; + if (*con == uc) { + *con = uc->next; ret = true; break; } @@ -558,8 +558,8 @@ static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *consumer return ret; } -static int __copy_insn(struct address_space *mapping, - struct vm_area_struct *vma, char *insn, +static int +__copy_insn(struct address_space *mapping, struct vm_area_struct *vma, char *insn, unsigned long nbytes, unsigned long offset) { struct file *filp = vma->vm_file; @@ -590,7 +590,8 @@ static int __copy_insn(struct address_space *mapping, return 0; } -static int copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr) +static int +copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr) { struct address_space *mapping; unsigned long nbytes; @@ -617,8 +618,9 @@ static int copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned return __copy_insn(mapping, vma, uprobe->arch.insn, bytes, uprobe->offset); } -static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, - struct vm_area_struct *vma, loff_t vaddr) +static int +install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, + struct vm_area_struct *vma, loff_t vaddr) { unsigned long addr; int ret; @@ -643,20 +645,21 @@ static int install_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, if (is_bkpt_insn((uprobe_opcode_t *)uprobe->arch.insn)) return -EEXIST; - ret = arch_uprobes_analyze_insn(mm, &uprobe->arch); + ret = arch_uprobes_analyze_insn(&uprobe->arch, mm); if (ret) return ret; uprobe->flags |= UPROBE_COPY_INSN; } - ret = set_bkpt(mm, &uprobe->arch, addr); + ret = set_bkpt(&uprobe->arch, mm, addr); return ret; } -static void remove_breakpoint(struct mm_struct *mm, struct uprobe *uprobe, loff_t vaddr) +static void +remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, loff_t vaddr) { - set_orig_insn(mm, &uprobe->arch, (unsigned long)vaddr, true); + set_orig_insn(&uprobe->arch, mm, (unsigned long)vaddr, true); } static void delete_uprobe(struct uprobe *uprobe) @@ -671,9 +674,9 @@ static void delete_uprobe(struct uprobe *uprobe) atomic_dec(&uprobe_events); } -static struct vma_info *__find_next_vma_info(struct list_head *head, - loff_t offset, struct address_space *mapping, - struct vma_info *vi, bool is_register) +static struct vma_info * +__find_next_vma_info(struct address_space *mapping, struct list_head *head, + struct vma_info *vi, loff_t offset, bool is_register) { struct prio_tree_iter iter; struct vm_area_struct *vma; @@ -719,8 +722,8 @@ static struct vma_info *__find_next_vma_info(struct list_head *head, * yet been inserted. */ static struct vma_info * -find_next_vma_info(struct list_head *head, loff_t offset, struct address_space *mapping, - bool is_register) +find_next_vma_info(struct address_space *mapping, struct list_head *head, + loff_t offset, bool is_register) { struct vma_info *vi, *retvi; @@ -729,7 +732,7 @@ find_next_vma_info(struct list_head *head, loff_t offset, struct address_space * return ERR_PTR(-ENOMEM); mutex_lock(&mapping->i_mmap_mutex); - retvi = __find_next_vma_info(head, offset, mapping, vi, is_register); + retvi = __find_next_vma_info(mapping, head, vi, offset, is_register); mutex_unlock(&mapping->i_mmap_mutex); if (!retvi) @@ -754,7 +757,7 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register) ret = 0; for (;;) { - vi = find_next_vma_info(&try_list, uprobe->offset, mapping, is_register); + vi = find_next_vma_info(mapping, &try_list, uprobe->offset, is_register); if (!vi) break; @@ -784,9 +787,9 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register) } if (is_register) - ret = install_breakpoint(mm, uprobe, vma, vi->vaddr); + ret = install_breakpoint(uprobe, mm, vma, vi->vaddr); else - remove_breakpoint(mm, uprobe, vi->vaddr); + remove_breakpoint(uprobe, mm, vi->vaddr); up_read(&mm->mmap_sem); mmput(mm); @@ -823,25 +826,25 @@ static void __uprobe_unregister(struct uprobe *uprobe) * uprobe_register - register a probe * @inode: the file in which the probe has to be placed. * @offset: offset from the start of the file. - * @consumer: information on howto handle the probe.. + * @uc: information on howto handle the probe.. * * Apart from the access refcount, uprobe_register() takes a creation * refcount (thro alloc_uprobe) if and only if this @uprobe is getting * inserted into the rbtree (i.e first consumer for a @inode:@offset * tuple). Creation refcount stops uprobe_unregister from freeing the * @uprobe even before the register operation is complete. Creation - * refcount is released when the last @consumer for the @uprobe + * refcount is released when the last @uc for the @uprobe * unregisters. * * Return errno if it cannot successully install probes * else return 0 (success) */ -int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) +int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) { struct uprobe *uprobe; int ret; - if (!inode || !consumer || consumer->next) + if (!inode || !uc || uc->next) return -EINVAL; if (offset > i_size_read(inode)) @@ -851,7 +854,7 @@ int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer * mutex_lock(uprobes_hash(inode)); uprobe = alloc_uprobe(inode, offset); - if (uprobe && !consumer_add(uprobe, consumer)) { + if (uprobe && !consumer_add(uprobe, uc)) { ret = __uprobe_register(uprobe); if (ret) { uprobe->consumers = NULL; @@ -871,13 +874,13 @@ int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer * * uprobe_unregister - unregister a already registered probe. * @inode: the file in which the probe has to be removed. * @offset: offset from the start of the file. - * @consumer: identify which probe if multiple probes are colocated. + * @uc: identify which probe if multiple probes are colocated. */ -void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *consumer) +void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) { struct uprobe *uprobe; - if (!inode || !consumer) + if (!inode || !uc) return; uprobe = find_uprobe(inode, offset); @@ -886,7 +889,7 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consume mutex_lock(uprobes_hash(inode)); - if (consumer_del(uprobe, consumer)) { + if (consumer_del(uprobe, uc)) { if (!uprobe->consumers) { __uprobe_unregister(uprobe); uprobe->flags &= ~UPROBE_RUN_HANDLER; @@ -993,7 +996,7 @@ int uprobe_mmap(struct vm_area_struct *vma) if (!ret) { vaddr = vma_address(vma, uprobe->offset); if (vaddr >= vma->vm_start && vaddr < vma->vm_end) { - ret = install_breakpoint(vma->vm_mm, uprobe, vma, vaddr); + ret = install_breakpoint(uprobe, vma->vm_mm, vma, vaddr); /* Ignore double add: */ if (ret == -EEXIST) ret = 0; -- cgit v1.2.3 From 5cb4ac3a583d4ee18c8682ab857e093c4a0d0895 Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Mon, 12 Mar 2012 14:55:45 +0530 Subject: uprobes/core: Rename bkpt to swbp bkpt doesnt seem to be a correct abbrevation for breakpoint. Choice was between bp and breakpoint. Since bp can refer to things other than breakpoint, use swbp to refer to breakpoints. This is pure cleanup, no functional change intended. Signed-off-by: Srikar Dronamraju Cc: Linus Torvalds Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Linux-mm Cc: Oleg Nesterov Cc: Andi Kleen Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Arnaldo Carvalho de Melo Cc: Masami Hiramatsu Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120312092545.5379.91251.sendpatchset@srdronam.in.ibm.com Signed-off-by: Ingo Molnar --- arch/x86/include/asm/uprobes.h | 4 ++-- include/linux/uprobes.h | 4 ++-- kernel/events/uprobes.c | 34 +++++++++++++++++----------------- 3 files changed, 21 insertions(+), 21 deletions(-) (limited to 'arch') diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index 384f1bebf884..0500391f57d0 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h @@ -28,8 +28,8 @@ typedef u8 uprobe_opcode_t; #define MAX_UINSN_BYTES 16 #define UPROBE_XOL_SLOT_BYTES 128 /* to keep it cache aligned */ -#define UPROBE_BKPT_INSN 0xcc -#define UPROBE_BKPT_INSN_SIZE 1 +#define UPROBE_SWBP_INSN 0xcc +#define UPROBE_SWBP_INSN_SIZE 1 struct arch_uprobe { u16 fixups; diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index 58699182e9a7..eac525f41b94 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -52,9 +52,9 @@ struct uprobe_consumer { }; #ifdef CONFIG_UPROBES -extern int __weak set_bkpt(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); +extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr, bool verify); -extern bool __weak is_bkpt_insn(uprobe_opcode_t *insn); +extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_mmap(struct vm_area_struct *vma); diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index 9c5ddff1c8da..e56e56aa7535 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -170,14 +170,14 @@ out: } /** - * is_bkpt_insn - check if instruction is breakpoint instruction. + * is_swbp_insn - check if instruction is breakpoint instruction. * @insn: instruction to be checked. - * Default implementation of is_bkpt_insn + * Default implementation of is_swbp_insn * Returns true if @insn is a breakpoint instruction. */ -bool __weak is_bkpt_insn(uprobe_opcode_t *insn) +bool __weak is_swbp_insn(uprobe_opcode_t *insn) { - return *insn == UPROBE_BKPT_INSN; + return *insn == UPROBE_SWBP_INSN; } /* @@ -227,7 +227,7 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, * adding probes in write mapped pages since the breakpoints * might end up in the file copy. */ - if (!valid_vma(vma, is_bkpt_insn(&opcode))) + if (!valid_vma(vma, is_swbp_insn(&opcode))) goto put_out; uprobe = container_of(auprobe, struct uprobe, arch); @@ -259,8 +259,8 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm, /* poke the new insn in, ASSUMES we don't cross page boundary */ vaddr &= ~PAGE_MASK; - BUG_ON(vaddr + UPROBE_BKPT_INSN_SIZE > PAGE_SIZE); - memcpy(vaddr_new + vaddr, &opcode, UPROBE_BKPT_INSN_SIZE); + BUG_ON(vaddr + UPROBE_SWBP_INSN_SIZE > PAGE_SIZE); + memcpy(vaddr_new + vaddr, &opcode, UPROBE_SWBP_INSN_SIZE); kunmap_atomic(vaddr_new); kunmap_atomic(vaddr_old); @@ -308,7 +308,7 @@ static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_ lock_page(page); vaddr_new = kmap_atomic(page); vaddr &= ~PAGE_MASK; - memcpy(opcode, vaddr_new + vaddr, UPROBE_BKPT_INSN_SIZE); + memcpy(opcode, vaddr_new + vaddr, UPROBE_SWBP_INSN_SIZE); kunmap_atomic(vaddr_new); unlock_page(page); @@ -317,7 +317,7 @@ static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_ return 0; } -static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) +static int is_swbp_at_addr(struct mm_struct *mm, unsigned long vaddr) { uprobe_opcode_t opcode; int result; @@ -326,14 +326,14 @@ static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) if (result) return result; - if (is_bkpt_insn(&opcode)) + if (is_swbp_insn(&opcode)) return 1; return 0; } /** - * set_bkpt - store breakpoint at a given address. + * set_swbp - store breakpoint at a given address. * @auprobe: arch specific probepoint information. * @mm: the probed process address space. * @vaddr: the virtual address to insert the opcode. @@ -341,18 +341,18 @@ static int is_bkpt_at_addr(struct mm_struct *mm, unsigned long vaddr) * For mm @mm, store the breakpoint instruction at @vaddr. * Return 0 (success) or a negative errno. */ -int __weak set_bkpt(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) +int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr) { int result; - result = is_bkpt_at_addr(mm, vaddr); + result = is_swbp_at_addr(mm, vaddr); if (result == 1) return -EEXIST; if (result) return result; - return write_opcode(auprobe, mm, vaddr, UPROBE_BKPT_INSN); + return write_opcode(auprobe, mm, vaddr, UPROBE_SWBP_INSN); } /** @@ -371,7 +371,7 @@ set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long v if (verify) { int result; - result = is_bkpt_at_addr(mm, vaddr); + result = is_swbp_at_addr(mm, vaddr); if (!result) return -EINVAL; @@ -642,7 +642,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, if (ret) return ret; - if (is_bkpt_insn((uprobe_opcode_t *)uprobe->arch.insn)) + if (is_swbp_insn((uprobe_opcode_t *)uprobe->arch.insn)) return -EEXIST; ret = arch_uprobes_analyze_insn(&uprobe->arch, mm); @@ -651,7 +651,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, uprobe->flags |= UPROBE_COPY_INSN; } - ret = set_bkpt(&uprobe->arch, mm, addr); + ret = set_swbp(&uprobe->arch, mm, addr); return ret; } -- cgit v1.2.3 From 0326f5a94ddea33fa331b2519f4172f4fb387baa Mon Sep 17 00:00:00 2001 From: Srikar Dronamraju Date: Tue, 13 Mar 2012 23:30:11 +0530 Subject: uprobes/core: Handle breakpoint and singlestep exceptions Uprobes uses exception notifiers to get to know if a thread hit a breakpoint or a singlestep exception. When a thread hits a uprobe or is singlestepping post a uprobe hit, the uprobe exception notifier sets its TIF_UPROBE bit, which will then be checked on its return to userspace path (do_notify_resume() ->uprobe_notify_resume()), where the consumers handlers are run (in task context) based on the defined filters. Uprobe hits are thread specific and hence we need to maintain information about if a task hit a uprobe, what uprobe was hit, the slot where the original instruction was copied for xol so that it can be singlestepped with appropriate fixups. In some cases, special care is needed for instructions that are executed out of line (xol). These are architecture specific artefacts, such as handling RIP relative instructions on x86_64. Since the instruction at which the uprobe was inserted is executed out of line, architecture specific fixups are added so that the thread continues normal execution in the presence of a uprobe. Postpone the signals until we execute the probed insn. post_xol() path does a recalc_sigpending() before return to user-mode, this ensures the signal can't be lost. Uprobes relies on DIE_DEBUG notification to notify if a singlestep is complete. Adds x86 specific uprobe exception notifiers and appropriate hooks needed to determine a uprobe hit and subsequent post processing. Add requisite x86 fixups for xol for uprobes. Specific cases needing fixups include relative jumps (x86_64), calls, etc. Where possible, we check and skip singlestepping the breakpointed instructions. For now we skip single byte as well as few multibyte nop instructions. However this can be extended to other instructions too. Credits to Oleg Nesterov for suggestions/patches related to signal, breakpoint, singlestep handling code. Signed-off-by: Srikar Dronamraju Cc: Linus Torvalds Cc: Ananth N Mavinakayanahalli Cc: Jim Keniston Cc: Linux-mm Cc: Oleg Nesterov Cc: Andi Kleen Cc: Christoph Hellwig Cc: Steven Rostedt Cc: Arnaldo Carvalho de Melo Cc: Masami Hiramatsu Cc: Peter Zijlstra Link: http://lkml.kernel.org/r/20120313180011.29771.89027.sendpatchset@srdronam.in.ibm.com [ Performed various cleanliness edits ] Signed-off-by: Ingo Molnar --- arch/x86/include/asm/thread_info.h | 2 + arch/x86/include/asm/uprobes.h | 16 +- arch/x86/kernel/signal.c | 6 + arch/x86/kernel/uprobes.c | 265 +++++++++++++++++++++++++++++- include/linux/sched.h | 4 + include/linux/uprobes.h | 55 ++++++- kernel/events/uprobes.c | 323 ++++++++++++++++++++++++++++++++++++- kernel/fork.c | 4 + kernel/signal.c | 4 + 9 files changed, 664 insertions(+), 15 deletions(-) (limited to 'arch') diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h index ad6df8ccd715..0710c11305d4 100644 --- a/arch/x86/include/asm/thread_info.h +++ b/arch/x86/include/asm/thread_info.h @@ -85,6 +85,7 @@ struct thread_info { #define TIF_SECCOMP 8 /* secure computing */ #define TIF_MCE_NOTIFY 10 /* notify userspace of an MCE */ #define TIF_USER_RETURN_NOTIFY 11 /* notify kernel of userspace return */ +#define TIF_UPROBE 12 /* breakpointed or singlestepping */ #define TIF_NOTSC 16 /* TSC is not accessible in userland */ #define TIF_IA32 17 /* IA32 compatibility process */ #define TIF_FORK 18 /* ret_from_fork */ @@ -109,6 +110,7 @@ struct thread_info { #define _TIF_SECCOMP (1 << TIF_SECCOMP) #define _TIF_MCE_NOTIFY (1 << TIF_MCE_NOTIFY) #define _TIF_USER_RETURN_NOTIFY (1 << TIF_USER_RETURN_NOTIFY) +#define _TIF_UPROBE (1 << TIF_UPROBE) #define _TIF_NOTSC (1 << TIF_NOTSC) #define _TIF_IA32 (1 << TIF_IA32) #define _TIF_FORK (1 << TIF_FORK) diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h index 0500391f57d0..1e9bed14f7ae 100644 --- a/arch/x86/include/asm/uprobes.h +++ b/arch/x86/include/asm/uprobes.h @@ -23,6 +23,8 @@ * Jim Keniston */ +#include + typedef u8 uprobe_opcode_t; #define MAX_UINSN_BYTES 16 @@ -39,5 +41,17 @@ struct arch_uprobe { #endif }; -extern int arch_uprobes_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm); +struct arch_uprobe_task { + unsigned long saved_trap_nr; +#ifdef CONFIG_X86_64 + unsigned long saved_scratch_register; +#endif +}; + +extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm); +extern int arch_uprobe_pre_xol(struct arch_uprobe *aup, struct pt_regs *regs); +extern int arch_uprobe_post_xol(struct arch_uprobe *aup, struct pt_regs *regs); +extern bool arch_uprobe_xol_was_trapped(struct task_struct *tsk); +extern int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data); +extern void arch_uprobe_abort_xol(struct arch_uprobe *aup, struct pt_regs *regs); #endif /* _ASM_UPROBES_H */ diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c index 9c73acc1c860..b3cd6913ceea 100644 --- a/arch/x86/kernel/signal.c +++ b/arch/x86/kernel/signal.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -823,6 +824,11 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags) mce_notify_process(); #endif /* CONFIG_X86_64 && CONFIG_X86_MCE */ + if (thread_info_flags & _TIF_UPROBE) { + clear_thread_flag(TIF_UPROBE); + uprobe_notify_resume(regs); + } + /* deal with pending signal delivery */ if (thread_info_flags & _TIF_SIGPENDING) do_signal(regs); diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c index 851a11b0d38c..dc4e910a7d96 100644 --- a/arch/x86/kernel/uprobes.c +++ b/arch/x86/kernel/uprobes.c @@ -24,22 +24,28 @@ #include #include #include +#include #include +#include #include /* Post-execution fixups. */ /* No fixup needed */ -#define UPROBE_FIX_NONE 0x0 +#define UPROBE_FIX_NONE 0x0 + /* Adjust IP back to vicinity of actual insn */ #define UPROBE_FIX_IP 0x1 + /* Adjust the return address of a call insn */ #define UPROBE_FIX_CALL 0x2 #define UPROBE_FIX_RIP_AX 0x8000 #define UPROBE_FIX_RIP_CX 0x4000 +#define UPROBE_TRAP_NR UINT_MAX + /* Adaptations for mhiramat x86 decoder v14. */ #define OPCODE1(insn) ((insn)->opcode.bytes[0]) #define OPCODE2(insn) ((insn)->opcode.bytes[1]) @@ -221,10 +227,9 @@ static int validate_insn_32bits(struct arch_uprobe *auprobe, struct insn *insn) } /* - * Figure out which fixups post_xol() will need to perform, and annotate - * arch_uprobe->fixups accordingly. To start with, - * arch_uprobe->fixups is either zero or it reflects rip-related - * fixups. + * Figure out which fixups arch_uprobe_post_xol() will need to perform, and + * annotate arch_uprobe->fixups accordingly. To start with, + * arch_uprobe->fixups is either zero or it reflects rip-related fixups. */ static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn) { @@ -401,12 +406,12 @@ static int validate_insn_bits(struct arch_uprobe *auprobe, struct mm_struct *mm, #endif /* CONFIG_X86_64 */ /** - * arch_uprobes_analyze_insn - instruction analysis including validity and fixups. + * arch_uprobe_analyze_insn - instruction analysis including validity and fixups. * @mm: the probed address space. * @arch_uprobe: the probepoint information. * Return 0 on success or a -ve number on error. */ -int arch_uprobes_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm) +int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm) { int ret; struct insn insn; @@ -421,3 +426,249 @@ int arch_uprobes_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm) return 0; } + +#ifdef CONFIG_X86_64 +/* + * If we're emulating a rip-relative instruction, save the contents + * of the scratch register and store the target address in that register. + */ +static void +pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs, + struct arch_uprobe_task *autask) +{ + if (auprobe->fixups & UPROBE_FIX_RIP_AX) { + autask->saved_scratch_register = regs->ax; + regs->ax = current->utask->vaddr; + regs->ax += auprobe->rip_rela_target_address; + } else if (auprobe->fixups & UPROBE_FIX_RIP_CX) { + autask->saved_scratch_register = regs->cx; + regs->cx = current->utask->vaddr; + regs->cx += auprobe->rip_rela_target_address; + } +} +#else +static void +pre_xol_rip_insn(struct arch_uprobe *auprobe, struct pt_regs *regs, + struct arch_uprobe_task *autask) +{ + /* No RIP-relative addressing on 32-bit */ +} +#endif + +/* + * arch_uprobe_pre_xol - prepare to execute out of line. + * @auprobe: the probepoint information. + * @regs: reflects the saved user state of current task. + */ +int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct arch_uprobe_task *autask; + + autask = ¤t->utask->autask; + autask->saved_trap_nr = current->thread.trap_nr; + current->thread.trap_nr = UPROBE_TRAP_NR; + regs->ip = current->utask->xol_vaddr; + pre_xol_rip_insn(auprobe, regs, autask); + + return 0; +} + +/* + * This function is called by arch_uprobe_post_xol() to adjust the return + * address pushed by a call instruction executed out of line. + */ +static int adjust_ret_addr(unsigned long sp, long correction) +{ + int rasize, ncopied; + long ra = 0; + + if (is_ia32_task()) + rasize = 4; + else + rasize = 8; + + ncopied = copy_from_user(&ra, (void __user *)sp, rasize); + if (unlikely(ncopied)) + return -EFAULT; + + ra += correction; + ncopied = copy_to_user((void __user *)sp, &ra, rasize); + if (unlikely(ncopied)) + return -EFAULT; + + return 0; +} + +#ifdef CONFIG_X86_64 +static bool is_riprel_insn(struct arch_uprobe *auprobe) +{ + return ((auprobe->fixups & (UPROBE_FIX_RIP_AX | UPROBE_FIX_RIP_CX)) != 0); +} + +static void +handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, long *correction) +{ + if (is_riprel_insn(auprobe)) { + struct arch_uprobe_task *autask; + + autask = ¤t->utask->autask; + if (auprobe->fixups & UPROBE_FIX_RIP_AX) + regs->ax = autask->saved_scratch_register; + else + regs->cx = autask->saved_scratch_register; + + /* + * The original instruction includes a displacement, and so + * is 4 bytes longer than what we've just single-stepped. + * Fall through to handle stuff like "jmpq *...(%rip)" and + * "callq *...(%rip)". + */ + if (correction) + *correction += 4; + } +} +#else +static void +handle_riprel_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs, long *correction) +{ + /* No RIP-relative addressing on 32-bit */ +} +#endif + +/* + * If xol insn itself traps and generates a signal(Say, + * SIGILL/SIGSEGV/etc), then detect the case where a singlestepped + * instruction jumps back to its own address. It is assumed that anything + * like do_page_fault/do_trap/etc sets thread.trap_nr != -1. + * + * arch_uprobe_pre_xol/arch_uprobe_post_xol save/restore thread.trap_nr, + * arch_uprobe_xol_was_trapped() simply checks that ->trap_nr is not equal to + * UPROBE_TRAP_NR == -1 set by arch_uprobe_pre_xol(). + */ +bool arch_uprobe_xol_was_trapped(struct task_struct *t) +{ + if (t->thread.trap_nr != UPROBE_TRAP_NR) + return true; + + return false; +} + +/* + * Called after single-stepping. To avoid the SMP problems that can + * occur when we temporarily put back the original opcode to + * single-step, we single-stepped a copy of the instruction. + * + * This function prepares to resume execution after the single-step. + * We have to fix things up as follows: + * + * Typically, the new ip is relative to the copied instruction. We need + * to make it relative to the original instruction (FIX_IP). Exceptions + * are return instructions and absolute or indirect jump or call instructions. + * + * If the single-stepped instruction was a call, the return address that + * is atop the stack is the address following the copied instruction. We + * need to make it the address following the original instruction (FIX_CALL). + * + * If the original instruction was a rip-relative instruction such as + * "movl %edx,0xnnnn(%rip)", we have instead executed an equivalent + * instruction using a scratch register -- e.g., "movl %edx,(%rax)". + * We need to restore the contents of the scratch register and adjust + * the ip, keeping in mind that the instruction we executed is 4 bytes + * shorter than the original instruction (since we squeezed out the offset + * field). (FIX_RIP_AX or FIX_RIP_CX) + */ +int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct uprobe_task *utask; + long correction; + int result = 0; + + WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR); + + utask = current->utask; + current->thread.trap_nr = utask->autask.saved_trap_nr; + correction = (long)(utask->vaddr - utask->xol_vaddr); + handle_riprel_post_xol(auprobe, regs, &correction); + if (auprobe->fixups & UPROBE_FIX_IP) + regs->ip += correction; + + if (auprobe->fixups & UPROBE_FIX_CALL) + result = adjust_ret_addr(regs->sp, correction); + + return result; +} + +/* callback routine for handling exceptions. */ +int arch_uprobe_exception_notify(struct notifier_block *self, unsigned long val, void *data) +{ + struct die_args *args = data; + struct pt_regs *regs = args->regs; + int ret = NOTIFY_DONE; + + /* We are only interested in userspace traps */ + if (regs && !user_mode_vm(regs)) + return NOTIFY_DONE; + + switch (val) { + case DIE_INT3: + if (uprobe_pre_sstep_notifier(regs)) + ret = NOTIFY_STOP; + + break; + + case DIE_DEBUG: + if (uprobe_post_sstep_notifier(regs)) + ret = NOTIFY_STOP; + + default: + break; + } + + return ret; +} + +/* + * This function gets called when XOL instruction either gets trapped or + * the thread has a fatal signal, so reset the instruction pointer to its + * probed address. + */ +void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + struct uprobe_task *utask = current->utask; + + current->thread.trap_nr = utask->autask.saved_trap_nr; + handle_riprel_post_xol(auprobe, regs, NULL); + instruction_pointer_set(regs, utask->vaddr); +} + +/* + * Skip these instructions as per the currently known x86 ISA. + * 0x66* { 0x90 | 0x0f 0x1f | 0x0f 0x19 | 0x87 0xc0 } + */ +bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) +{ + int i; + + for (i = 0; i < MAX_UINSN_BYTES; i++) { + if ((auprobe->insn[i] == 0x66)) + continue; + + if (auprobe->insn[i] == 0x90) + return true; + + if (i == (MAX_UINSN_BYTES - 1)) + break; + + if ((auprobe->insn[i] == 0x0f) && (auprobe->insn[i+1] == 0x1f)) + return true; + + if ((auprobe->insn[i] == 0x0f) && (auprobe->insn[i+1] == 0x19)) + return true; + + if ((auprobe->insn[i] == 0x87) && (auprobe->insn[i+1] == 0xc0)) + return true; + + break; + } + return false; +} diff --git a/include/linux/sched.h b/include/linux/sched.h index 7d379a6bfd88..8379e3771690 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1590,6 +1590,10 @@ struct task_struct { #ifdef CONFIG_HAVE_HW_BREAKPOINT atomic_t ptrace_bp_refcnt; #endif +#ifdef CONFIG_UPROBES + struct uprobe_task *utask; + int uprobe_srcu_id; +#endif }; /* Future-safe accessor for struct task_struct's cpus_allowed. */ diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h index eac525f41b94..5ec778fdce6f 100644 --- a/include/linux/uprobes.h +++ b/include/linux/uprobes.h @@ -28,8 +28,9 @@ #include struct vm_area_struct; + #ifdef CONFIG_ARCH_SUPPORTS_UPROBES -#include +# include #endif /* flags that denote/change uprobes behaviour */ @@ -39,6 +40,8 @@ struct vm_area_struct; /* Dont run handlers when first register/ last unregister in progress*/ #define UPROBE_RUN_HANDLER 0x2 +/* Can skip singlestep */ +#define UPROBE_SKIP_SSTEP 0x4 struct uprobe_consumer { int (*handler)(struct uprobe_consumer *self, struct pt_regs *regs); @@ -52,13 +55,42 @@ struct uprobe_consumer { }; #ifdef CONFIG_UPROBES +enum uprobe_task_state { + UTASK_RUNNING, + UTASK_BP_HIT, + UTASK_SSTEP, + UTASK_SSTEP_ACK, + UTASK_SSTEP_TRAPPED, +}; + +/* + * uprobe_task: Metadata of a task while it singlesteps. + */ +struct uprobe_task { + enum uprobe_task_state state; + struct arch_uprobe_task autask; + + struct uprobe *active_uprobe; + + unsigned long xol_vaddr; + unsigned long vaddr; +}; + extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr); extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr, bool verify); extern bool __weak is_swbp_insn(uprobe_opcode_t *insn); extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc); extern int uprobe_mmap(struct vm_area_struct *vma); -#else /* CONFIG_UPROBES is not defined */ +extern void uprobe_free_utask(struct task_struct *t); +extern void uprobe_copy_process(struct task_struct *t); +extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs); +extern int uprobe_post_sstep_notifier(struct pt_regs *regs); +extern int uprobe_pre_sstep_notifier(struct pt_regs *regs); +extern void uprobe_notify_resume(struct pt_regs *regs); +extern bool uprobe_deny_signal(void); +extern bool __weak arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs); +#else /* !CONFIG_UPROBES */ static inline int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc) { @@ -72,5 +104,22 @@ static inline int uprobe_mmap(struct vm_area_struct *vma) { return 0; } -#endif /* CONFIG_UPROBES */ +static inline void uprobe_notify_resume(struct pt_regs *regs) +{ +} +static inline bool uprobe_deny_signal(void) +{ + return false; +} +static inline unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) +{ + return 0; +} +static inline void uprobe_free_utask(struct task_struct *t) +{ +} +static inline void uprobe_copy_process(struct task_struct *t) +{ +} +#endif /* !CONFIG_UPROBES */ #endif /* _LINUX_UPROBES_H */ diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c index e56e56aa7535..b807d1566b64 100644 --- a/kernel/events/uprobes.c +++ b/kernel/events/uprobes.c @@ -30,9 +30,12 @@ #include /* anon_vma_prepare */ #include /* set_pte_at_notify */ #include /* try_to_free_swap */ +#include /* user_enable_single_step */ +#include /* notifier mechanism */ #include +static struct srcu_struct uprobes_srcu; static struct rb_root uprobes_tree = RB_ROOT; static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */ @@ -486,6 +489,9 @@ static struct uprobe *insert_uprobe(struct uprobe *uprobe) u = __insert_uprobe(uprobe); spin_unlock_irqrestore(&uprobes_treelock, flags); + /* For now assume that the instruction need not be single-stepped */ + uprobe->flags |= UPROBE_SKIP_SSTEP; + return u; } @@ -523,6 +529,21 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) return uprobe; } +static void handler_chain(struct uprobe *uprobe, struct pt_regs *regs) +{ + struct uprobe_consumer *uc; + + if (!(uprobe->flags & UPROBE_RUN_HANDLER)) + return; + + down_read(&uprobe->consumer_rwsem); + for (uc = uprobe->consumers; uc; uc = uc->next) { + if (!uc->filter || uc->filter(uc, current)) + uc->handler(uc, regs); + } + up_read(&uprobe->consumer_rwsem); +} + /* Returns the previous consumer */ static struct uprobe_consumer * consumer_add(struct uprobe *uprobe, struct uprobe_consumer *uc) @@ -645,7 +666,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, if (is_swbp_insn((uprobe_opcode_t *)uprobe->arch.insn)) return -EEXIST; - ret = arch_uprobes_analyze_insn(&uprobe->arch, mm); + ret = arch_uprobe_analyze_insn(&uprobe->arch, mm); if (ret) return ret; @@ -662,10 +683,21 @@ remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, loff_t vaddr) set_orig_insn(&uprobe->arch, mm, (unsigned long)vaddr, true); } +/* + * There could be threads that have hit the breakpoint and are entering the + * notifier code and trying to acquire the uprobes_treelock. The thread + * calling delete_uprobe() that is removing the uprobe from the rb_tree can + * race with these threads and might acquire the uprobes_treelock compared + * to some of the breakpoint hit threads. In such a case, the breakpoint + * hit threads will not find the uprobe. The current unregistering thread + * waits till all other threads have hit a breakpoint, to acquire the + * uprobes_treelock before the uprobe is removed from the rbtree. + */ static void delete_uprobe(struct uprobe *uprobe) { unsigned long flags; + synchronize_srcu(&uprobes_srcu); spin_lock_irqsave(&uprobes_treelock, flags); rb_erase(&uprobe->rb_node, &uprobes_tree); spin_unlock_irqrestore(&uprobes_treelock, flags); @@ -1010,6 +1042,288 @@ int uprobe_mmap(struct vm_area_struct *vma) return ret; } +/** + * uprobe_get_swbp_addr - compute address of swbp given post-swbp regs + * @regs: Reflects the saved state of the task after it has hit a breakpoint + * instruction. + * Return the address of the breakpoint instruction. + */ +unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs) +{ + return instruction_pointer(regs) - UPROBE_SWBP_INSN_SIZE; +} + +/* + * Called with no locks held. + * Called in context of a exiting or a exec-ing thread. + */ +void uprobe_free_utask(struct task_struct *t) +{ + struct uprobe_task *utask = t->utask; + + if (t->uprobe_srcu_id != -1) + srcu_read_unlock_raw(&uprobes_srcu, t->uprobe_srcu_id); + + if (!utask) + return; + + if (utask->active_uprobe) + put_uprobe(utask->active_uprobe); + + kfree(utask); + t->utask = NULL; +} + +/* + * Called in context of a new clone/fork from copy_process. + */ +void uprobe_copy_process(struct task_struct *t) +{ + t->utask = NULL; + t->uprobe_srcu_id = -1; +} + +/* + * Allocate a uprobe_task object for the task. + * Called when the thread hits a breakpoint for the first time. + * + * Returns: + * - pointer to new uprobe_task on success + * - NULL otherwise + */ +static struct uprobe_task *add_utask(void) +{ + struct uprobe_task *utask; + + utask = kzalloc(sizeof *utask, GFP_KERNEL); + if (unlikely(!utask)) + return NULL; + + utask->active_uprobe = NULL; + current->utask = utask; + return utask; +} + +/* Prepare to single-step probed instruction out of line. */ +static int +pre_ssout(struct uprobe *uprobe, struct pt_regs *regs, unsigned long vaddr) +{ + return -EFAULT; +} + +/* + * If we are singlestepping, then ensure this thread is not connected to + * non-fatal signals until completion of singlestep. When xol insn itself + * triggers the signal, restart the original insn even if the task is + * already SIGKILL'ed (since coredump should report the correct ip). This + * is even more important if the task has a handler for SIGSEGV/etc, The + * _same_ instruction should be repeated again after return from the signal + * handler, and SSTEP can never finish in this case. + */ +bool uprobe_deny_signal(void) +{ + struct task_struct *t = current; + struct uprobe_task *utask = t->utask; + + if (likely(!utask || !utask->active_uprobe)) + return false; + + WARN_ON_ONCE(utask->state != UTASK_SSTEP); + + if (signal_pending(t)) { + spin_lock_irq(&t->sighand->siglock); + clear_tsk_thread_flag(t, TIF_SIGPENDING); + spin_unlock_irq(&t->sighand->siglock); + + if (__fatal_signal_pending(t) || arch_uprobe_xol_was_trapped(t)) { + utask->state = UTASK_SSTEP_TRAPPED; + set_tsk_thread_flag(t, TIF_UPROBE); + set_tsk_thread_flag(t, TIF_NOTIFY_RESUME); + } + } + + return true; +} + +/* + * Avoid singlestepping the original instruction if the original instruction + * is a NOP or can be emulated. + */ +static bool can_skip_sstep(struct uprobe *uprobe, struct pt_regs *regs) +{ + if (arch_uprobe_skip_sstep(&uprobe->arch, regs)) + return true; + + uprobe->flags &= ~UPROBE_SKIP_SSTEP; + return false; +} + +/* + * Run handler and ask thread to singlestep. + * Ensure all non-fatal signals cannot interrupt thread while it singlesteps. + */ +static void handle_swbp(struct pt_regs *regs) +{ + struct vm_area_struct *vma; + struct uprobe_task *utask; + struct uprobe *uprobe; + struct mm_struct *mm; + unsigned long bp_vaddr; + + uprobe = NULL; + bp_vaddr = uprobe_get_swbp_addr(regs); + mm = current->mm; + down_read(&mm->mmap_sem); + vma = find_vma(mm, bp_vaddr); + + if (vma && vma->vm_start <= bp_vaddr && valid_vma(vma, false)) { + struct inode *inode; + loff_t offset; + + inode = vma->vm_file->f_mapping->host; + offset = bp_vaddr - vma->vm_start; + offset += (vma->vm_pgoff << PAGE_SHIFT); + uprobe = find_uprobe(inode, offset); + } + + srcu_read_unlock_raw(&uprobes_srcu, current->uprobe_srcu_id); + current->uprobe_srcu_id = -1; + up_read(&mm->mmap_sem); + + if (!uprobe) { + /* No matching uprobe; signal SIGTRAP. */ + send_sig(SIGTRAP, current, 0); + return; + } + + utask = current->utask; + if (!utask) { + utask = add_utask(); + /* Cannot allocate; re-execute the instruction. */ + if (!utask) + goto cleanup_ret; + } + utask->active_uprobe = uprobe; + handler_chain(uprobe, regs); + if (uprobe->flags & UPROBE_SKIP_SSTEP && can_skip_sstep(uprobe, regs)) + goto cleanup_ret; + + utask->state = UTASK_SSTEP; + if (!pre_ssout(uprobe, regs, bp_vaddr)) { + user_enable_single_step(current); + return; + } + +cleanup_ret: + if (utask) { + utask->active_uprobe = NULL; + utask->state = UTASK_RUNNING; + } + if (uprobe) { + if (!(uprobe->flags & UPROBE_SKIP_SSTEP)) + + /* + * cannot singlestep; cannot skip instruction; + * re-execute the instruction. + */ + instruction_pointer_set(regs, bp_vaddr); + + put_uprobe(uprobe); + } +} + +/* + * Perform required fix-ups and disable singlestep. + * Allow pending signals to take effect. + */ +static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs) +{ + struct uprobe *uprobe; + + uprobe = utask->active_uprobe; + if (utask->state == UTASK_SSTEP_ACK) + arch_uprobe_post_xol(&uprobe->arch, regs); + else if (utask->state == UTASK_SSTEP_TRAPPED) + arch_uprobe_abort_xol(&uprobe->arch, regs); + else + WARN_ON_ONCE(1); + + put_uprobe(uprobe); + utask->active_uprobe = NULL; + utask->state = UTASK_RUNNING; + user_disable_single_step(current); + + spin_lock_irq(¤t->sighand->siglock); + recalc_sigpending(); /* see uprobe_deny_signal() */ + spin_unlock_irq(¤t->sighand->siglock); +} + +/* + * On breakpoint hit, breakpoint notifier sets the TIF_UPROBE flag. (and on + * subsequent probe hits on the thread sets the state to UTASK_BP_HIT) and + * allows the thread to return from interrupt. + * + * On singlestep exception, singlestep notifier sets the TIF_UPROBE flag and + * also sets the state to UTASK_SSTEP_ACK and allows the thread to return from + * interrupt. + * + * While returning to userspace, thread notices the TIF_UPROBE flag and calls + * uprobe_notify_resume(). + */ +void uprobe_notify_resume(struct pt_regs *regs) +{ + struct uprobe_task *utask; + + utask = current->utask; + if (!utask || utask->state == UTASK_BP_HIT) + handle_swbp(regs); + else + handle_singlestep(utask, regs); +} + +/* + * uprobe_pre_sstep_notifier gets called from interrupt context as part of + * notifier mechanism. Set TIF_UPROBE flag and indicate breakpoint hit. + */ +int uprobe_pre_sstep_notifier(struct pt_regs *regs) +{ + struct uprobe_task *utask; + + if (!current->mm) + return 0; + + utask = current->utask; + if (utask) + utask->state = UTASK_BP_HIT; + + set_thread_flag(TIF_UPROBE); + current->uprobe_srcu_id = srcu_read_lock_raw(&uprobes_srcu); + + return 1; +} + +/* + * uprobe_post_sstep_notifier gets called in interrupt context as part of notifier + * mechanism. Set TIF_UPROBE flag and indicate completion of singlestep. + */ +int uprobe_post_sstep_notifier(struct pt_regs *regs) +{ + struct uprobe_task *utask = current->utask; + + if (!current->mm || !utask || !utask->active_uprobe) + /* task is currently not uprobed */ + return 0; + + utask->state = UTASK_SSTEP_ACK; + set_thread_flag(TIF_UPROBE); + return 1; +} + +static struct notifier_block uprobe_exception_nb = { + .notifier_call = arch_uprobe_exception_notify, + .priority = INT_MAX-1, /* notified after kprobes, kgdb */ +}; + static int __init init_uprobes(void) { int i; @@ -1018,12 +1332,13 @@ static int __init init_uprobes(void) mutex_init(&uprobes_mutex[i]); mutex_init(&uprobes_mmap_mutex[i]); } - return 0; + init_srcu_struct(&uprobes_srcu); + + return register_die_notifier(&uprobe_exception_nb); } +module_init(init_uprobes); static void __exit exit_uprobes(void) { } - -module_init(init_uprobes); module_exit(exit_uprobes); diff --git a/kernel/fork.c b/kernel/fork.c index e2cd3e2a5ae8..eb7b63334009 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -67,6 +67,7 @@ #include #include #include +#include #include #include @@ -701,6 +702,8 @@ void mm_release(struct task_struct *tsk, struct mm_struct *mm) exit_pi_state_list(tsk); #endif + uprobe_free_utask(tsk); + /* Get rid of any cached register state */ deactivate_mm(tsk, mm); @@ -1295,6 +1298,7 @@ static struct task_struct *copy_process(unsigned long clone_flags, INIT_LIST_HEAD(&p->pi_state_list); p->pi_state_cache = NULL; #endif + uprobe_copy_process(p); /* * sigaltstack should be cleared when sharing the same VM */ diff --git a/kernel/signal.c b/kernel/signal.c index 8511e39813c7..e93ff0a719a0 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -29,6 +29,7 @@ #include #include #include +#include #define CREATE_TRACE_POINTS #include @@ -2192,6 +2193,9 @@ int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka, struct signal_struct *signal = current->signal; int signr; + if (unlikely(uprobe_deny_signal())) + return 0; + relock: /* * We'll jump back here after any time we were stopped in TASK_STOPPED. -- cgit v1.2.3 From 7498176803ca57200ce658da3850abcabb5cff84 Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Tue, 4 Oct 2011 17:10:27 +0530 Subject: arm/dts: OMAP4: Add mmc controller nodes and board data Add OMAP mmc related device tree data for OMAP4. Currenly limited to only omap4-panda and omap4-sdp boards. Signed-off-by: Rajendra Nayak Signed-off-by: Benoit Cousson --- arch/arm/boot/dts/omap4-panda.dts | 22 ++++++++++++++++++++++ arch/arm/boot/dts/omap4-sdp.dts | 24 ++++++++++++++++++++++++ arch/arm/boot/dts/omap4.dtsi | 31 +++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) (limited to 'arch') diff --git a/arch/arm/boot/dts/omap4-panda.dts b/arch/arm/boot/dts/omap4-panda.dts index 29646dc97229..ea6f5bb8a120 100644 --- a/arch/arm/boot/dts/omap4-panda.dts +++ b/arch/arm/boot/dts/omap4-panda.dts @@ -52,3 +52,25 @@ &i2c4 { clock-frequency = <400000>; }; + +&mmc1 { + vmmc-supply = <&vmmc>; + ti,bus-width = <8>; +}; + +&mmc2 { + status = "disable"; +}; + +&mmc3 { + status = "disable"; +}; + +&mmc4 { + status = "disable"; +}; + +&mmc5 { + ti,non-removable; + ti,bus-width = <4>; +}; diff --git a/arch/arm/boot/dts/omap4-sdp.dts b/arch/arm/boot/dts/omap4-sdp.dts index 31938c1c3f14..67b2e98074c6 100644 --- a/arch/arm/boot/dts/omap4-sdp.dts +++ b/arch/arm/boot/dts/omap4-sdp.dts @@ -91,3 +91,27 @@ vdd-supply = <&vdd_eth>; }; }; + +&mmc1 { + vmmc-supply = <&vmmc>; + ti,bus-width = <8>; +}; + +&mmc2 { + vmmc-supply = <&vaux1>; + ti,bus-width = <8>; + ti,non-removable; +}; + +&mmc3 { + status = "disable"; +}; + +&mmc4 { + status = "disable"; +}; + +&mmc5 { + ti,bus-width = <4>; + ti,non-removable; +}; diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi index 6c584534c290..359c4979c8aa 100644 --- a/arch/arm/boot/dts/omap4.dtsi +++ b/arch/arm/boot/dts/omap4.dtsi @@ -241,5 +241,36 @@ ti,hwmods = "mcspi4"; ti,spi-num-cs = <1>; }; + + mmc1: mmc@4809c000 { + compatible = "ti,omap4-hsmmc"; + ti,hwmods = "mmc1"; + ti,dual-volt; + ti,needs-special-reset; + }; + + mmc2: mmc@480b4000 { + compatible = "ti,omap4-hsmmc"; + ti,hwmods = "mmc2"; + ti,needs-special-reset; + }; + + mmc3: mmc@480ad000 { + compatible = "ti,omap4-hsmmc"; + ti,hwmods = "mmc3"; + ti,needs-special-reset; + }; + + mmc4: mmc@480d1000 { + compatible = "ti,omap4-hsmmc"; + ti,hwmods = "mmc4"; + ti,needs-special-reset; + }; + + mmc5: mmc@480d5000 { + compatible = "ti,omap4-hsmmc"; + ti,hwmods = "mmc5"; + ti,needs-special-reset; + }; }; }; -- cgit v1.2.3 From b3431f5ba402a98a89b78a9408b4972d8870df4d Mon Sep 17 00:00:00 2001 From: Rajendra Nayak Date: Wed, 22 Feb 2012 17:42:27 +0530 Subject: arm/dts: OMAP3: Add mmc controller nodes and board data Add OMAP mmc related device tree data for OMAP3. Currenly limited to only omap3-beagle board. Signed-off-by: Rajendra Nayak Signed-off-by: Benoit Cousson --- arch/arm/boot/dts/omap3-beagle.dts | 14 ++++++++++++++ arch/arm/boot/dts/omap3.dtsi | 16 ++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'arch') diff --git a/arch/arm/boot/dts/omap3-beagle.dts b/arch/arm/boot/dts/omap3-beagle.dts index 54556b19c97d..8c756be4d7ad 100644 --- a/arch/arm/boot/dts/omap3-beagle.dts +++ b/arch/arm/boot/dts/omap3-beagle.dts @@ -53,3 +53,17 @@ reg = <0x50>; }; }; + +&mmc1 { + vmmc-supply = <&vmmc1>; + vmmc_aux-supply = <&vsim>; + ti,bus-width = <8>; +}; + +&mmc2 { + status = "disable"; +}; + +&mmc3 { + status = "disable"; +}; diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi index dc9425c48ed9..99474fa5fac4 100644 --- a/arch/arm/boot/dts/omap3.dtsi +++ b/arch/arm/boot/dts/omap3.dtsi @@ -199,5 +199,21 @@ ti,hwmods = "mcspi4"; ti,spi-num-cs = <1>; }; + + mmc1: mmc@4809c000 { + compatible = "ti,omap3-hsmmc"; + ti,hwmods = "mmc1"; + ti,dual-volt; + }; + + mmc2: mmc@480b4000 { + compatible = "ti,omap3-hsmmc"; + ti,hwmods = "mmc2"; + }; + + mmc3: mmc@480ad000 { + compatible = "ti,omap3-hsmmc"; + ti,hwmods = "mmc3"; + }; }; }; -- cgit v1.2.3 From a6fca40f1d7f3e232c9de27c1cebbb9f787fbc4f Mon Sep 17 00:00:00 2001 From: Suresh Siddha Date: Thu, 22 Mar 2012 17:01:25 -0700 Subject: x86, tlb: Switch cr3 in leave_mm() only when needed Currently leave_mm() unconditionally switches the cr3 to swapper_pg_dir. But there is no need to change the cr3, if we already left that mm. intel_idle() for example calls leave_mm() on every deep c-state entry where the CPU flushes the TLB for us. Similarly flush_tlb_all() was also calling leave_mm() whenever the TLB is in LAZY state. Both these paths will be improved with this change. Signed-off-by: Suresh Siddha Link: http://lkml.kernel.org/r/1332460885.16101.147.camel@sbsiddha-desk.sc.intel.com Signed-off-by: H. Peter Anvin --- arch/x86/mm/tlb.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'arch') diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c index d6c0418c3e47..125bcad1b757 100644 --- a/arch/x86/mm/tlb.c +++ b/arch/x86/mm/tlb.c @@ -61,11 +61,13 @@ static DEFINE_PER_CPU_READ_MOSTLY(int, tlb_vector_offset); */ void leave_mm(int cpu) { + struct mm_struct *active_mm = percpu_read(cpu_tlbstate.active_mm); if (percpu_read(cpu_tlbstate.state) == TLBSTATE_OK) BUG(); - cpumask_clear_cpu(cpu, - mm_cpumask(percpu_read(cpu_tlbstate.active_mm))); - load_cr3(swapper_pg_dir); + if (cpumask_test_cpu(cpu, mm_cpumask(active_mm))) { + cpumask_clear_cpu(cpu, mm_cpumask(active_mm)); + load_cr3(swapper_pg_dir); + } } EXPORT_SYMBOL_GPL(leave_mm); -- cgit v1.2.3 From 2e064b1e131eba262c0ba4268cb79dbc72edeece Mon Sep 17 00:00:00 2001 From: Jordan Justen Date: Fri, 23 Mar 2012 09:35:04 -0700 Subject: x86, efi: Fix issue of overlapping .reloc section for EFI_STUB Previously the .reloc section was embedded in the .text section. No relocations are required during the PE/COFF loading phase for the kernel using the EFI_STUB UEFI loader. To fix the issue of overlapping sections, create a .reloc section with a zero length. The .reloc section header must exist to make sure the image will be loaded by the UEFI firmware, but a zero-length section header seems to be sufficient. Signed-off-by: Jordan Justen Link: http://lkml.kernel.org/r/1332520506-6472-2-git-send-email-jordan.l.justen@intel.com Acked-by: Matt Fleming Signed-off-by: H. Peter Anvin --- arch/x86/boot/header.S | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) (limited to 'arch') diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S index f1bbeeb09148..4e9124b148c2 100644 --- a/arch/x86/boot/header.S +++ b/arch/x86/boot/header.S @@ -217,18 +217,17 @@ section_table: # # The EFI application loader requires a relocation section - # because EFI applications are relocatable and not having - # this section seems to confuse it. But since we don't need - # the loader to fixup any relocs for us just fill it with a - # single dummy reloc. + # because EFI applications must be relocatable. But since + # we don't need the loader to fixup any relocs for us, we + # just create an empty (zero-length) .reloc section header. # .ascii ".reloc" .byte 0 .byte 0 - .long reloc_end - reloc_start - .long reloc_start - .long reloc_end - reloc_start # SizeOfRawData - .long reloc_start # PointerToRawData + .long 0 + .long 0 + .long 0 # SizeOfRawData + .long 0 # PointerToRawData .long 0 # PointerToRelocations .long 0 # PointerToLineNumbers .word 0 # NumberOfRelocations @@ -469,10 +468,3 @@ setup_corrupt: .data dummy: .long 0 - - .section .reloc -reloc_start: - .long dummy - reloc_start - .long 10 - .word 0 -reloc_end: -- cgit v1.2.3 From e31be363df3092821bf179cf4baa076f501b8ae6 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 23 Mar 2012 09:35:05 -0700 Subject: x86, efi: Fix .text section overlapping image header for EFI_STUB This change modifes the PE .text section to start after the first sector of the kernel image. The header may be modified by the UEFI secure boot signing, so it is not appropriate for it to be included in one of the image sections. Since the sections are part of the secure boot hash, this modification to the .text section contents would invalidate the secure boot signed hash. Note: UEFI secure boot does hash the image header, but fields that are changed by the signing process are excluded from the hash calculation. This exclusion process is only handled for the image header, and not image sections. Luckily, we can still easily boot without the first sector by initializing a few fields in arch/x86/boot/compressed/eboot.c. Signed-off-by: Matt Fleming Link: http://lkml.kernel.org/r/1332520506-6472-3-git-send-email-jordan.l.justen@intel.com [jordan.l.justen@intel.com: set .text vma & file offset] Signed-off-by: Jordan Justen Signed-off-by: H. Peter Anvin --- arch/x86/boot/compressed/eboot.c | 14 +++++++++++--- arch/x86/boot/header.S | 2 +- arch/x86/boot/tools/build.c | 25 ++++++++++++++++++++++--- 3 files changed, 34 insertions(+), 7 deletions(-) (limited to 'arch') diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index fec216f4fbc3..01cbb8707a31 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -904,11 +904,19 @@ struct boot_params *efi_main(void *handle, efi_system_table_t *_table) memset(boot_params, 0x0, 0x4000); - /* Copy first two sectors to boot_params */ - memcpy(boot_params, image->image_base, 1024); - hdr = &boot_params->hdr; + /* Copy the second sector to boot_params */ + memcpy(&hdr->jump, image->image_base + 512, 512); + + /* + * Fill out some of the header fields ourselves because the + * EFI firmware loader doesn't load the first sector. + */ + hdr->root_flags = 1; + hdr->vid_mode = 0xffff; + hdr->boot_flag = 0xAA55; + /* * The EFI firmware loader could have placed the kernel image * anywhere in memory, but the kernel has various restrictions diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S index 4e9124b148c2..4ceb56e9a4ce 100644 --- a/arch/x86/boot/header.S +++ b/arch/x86/boot/header.S @@ -147,7 +147,7 @@ optional_header: # Filled in by build.c .long 0x0000 # AddressOfEntryPoint - .long 0x0000 # BaseOfCode + .long 0x0200 # BaseOfCode #ifdef CONFIG_X86_32 .long 0 # data #endif diff --git a/arch/x86/boot/tools/build.c b/arch/x86/boot/tools/build.c index 4e9bd6bcafa6..2aeab3dc9e5f 100644 --- a/arch/x86/boot/tools/build.c +++ b/arch/x86/boot/tools/build.c @@ -202,12 +202,19 @@ int main(int argc, char ** argv) pe_header = *(unsigned int *)&buf[0x3c]; - /* Size of code */ - *(unsigned int *)&buf[pe_header + 0x1c] = file_sz; - /* Size of image */ *(unsigned int *)&buf[pe_header + 0x50] = file_sz; + /* + * Subtract the size of the first section (512 bytes) which + * includes the header and .reloc section. The remaining size + * is that of the .text section. + */ + file_sz -= 512; + + /* Size of code */ + *(unsigned int *)&buf[pe_header + 0x1c] = file_sz; + #ifdef CONFIG_X86_32 /* Address of entry point */ *(unsigned int *)&buf[pe_header + 0x28] = i; @@ -215,8 +222,14 @@ int main(int argc, char ** argv) /* .text size */ *(unsigned int *)&buf[pe_header + 0xb0] = file_sz; + /* .text vma */ + *(unsigned int *)&buf[pe_header + 0xb4] = 0x200; + /* .text size of initialised data */ *(unsigned int *)&buf[pe_header + 0xb8] = file_sz; + + /* .text file offset */ + *(unsigned int *)&buf[pe_header + 0xbc] = 0x200; #else /* * Address of entry point. startup_32 is at the beginning and @@ -228,8 +241,14 @@ int main(int argc, char ** argv) /* .text size */ *(unsigned int *)&buf[pe_header + 0xc0] = file_sz; + /* .text vma */ + *(unsigned int *)&buf[pe_header + 0xc4] = 0x200; + /* .text size of initialised data */ *(unsigned int *)&buf[pe_header + 0xc8] = file_sz; + + /* .text file offset */ + *(unsigned int *)&buf[pe_header + 0xcc] = 0x200; #endif /* CONFIG_X86_32 */ #endif /* CONFIG_EFI_STUB */ -- cgit v1.2.3 From e47bb0bda46bf50f81671db502d0c903e0a32604 Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Fri, 23 Mar 2012 09:35:06 -0700 Subject: x86, efi: Fix NumberOfRvaAndSizes field in PE32 header for EFI_STUB We've actually got six data directories in the header, not one. Even though the firmware loader doesn't seem to mind, when we come to sign the kernel image the signing tool thinks that there is no Certificate Table data directory, even though we've allocated space for one. Signed-off-by: Matt Fleming Link: http://lkml.kernel.org/r/1332520506-6472-4-git-send-email-jordan.l.justen@intel.com Reviewed-by: Jordan Justen Signed-off-by: H. Peter Anvin --- arch/x86/boot/header.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S index 4ceb56e9a4ce..8bbea6aa40d9 100644 --- a/arch/x86/boot/header.S +++ b/arch/x86/boot/header.S @@ -189,7 +189,7 @@ extra_header_fields: .quad 0 # SizeOfHeapCommit #endif .long 0 # LoaderFlags - .long 0x1 # NumberOfRvaAndSizes + .long 0x6 # NumberOfRvaAndSizes .quad 0 # ExportTable .quad 0 # ImportTable -- cgit v1.2.3 From 35372a7d45291140a97518a8d1c8cb0e31ee2bb7 Mon Sep 17 00:00:00 2001 From: Richard Weinberger Date: Fri, 30 Mar 2012 01:38:03 +0200 Subject: x86: spinlock.h: Remove REG_PTR_MODE REG_PTR_MODE has no users at all. Signed-off-by: Richard Weinberger Link: http://lkml.kernel.org/r/1333064283-3109-1-git-send-email-richard@nod.at Acked-by: Acked-by: Jan Beulich Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/spinlock.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'arch') diff --git a/arch/x86/include/asm/spinlock.h b/arch/x86/include/asm/spinlock.h index 76bfa2cf301d..b315a33867f2 100644 --- a/arch/x86/include/asm/spinlock.h +++ b/arch/x86/include/asm/spinlock.h @@ -20,10 +20,8 @@ #ifdef CONFIG_X86_32 # define LOCK_PTR_REG "a" -# define REG_PTR_MODE "k" #else # define LOCK_PTR_REG "D" -# define REG_PTR_MODE "q" #endif #if defined(CONFIG_X86_32) && \ -- cgit v1.2.3 From a23dc694828e3de96bf18e20459ba885ba91cb29 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 16 Mar 2012 16:56:38 +0800 Subject: ASoC: imx: merge sound/soc/imx into sound/soc/fsl Freescale PowerPC and ARM/IMX families share the same SSI IP block. The patch merges sound/soc/imx into sound/soc/fsl, so that the possible code sharing and consolidation can happen. This is a plain merge, except that menuconfig SND_POWERPC_SOC is added in Kconfig for PowerPC platform as a correspondence to SND_IMX_SOC for IMX platform. Signed-off-by: Shawn Guo Acked-by: Sascha Hauer Acked-by: Timur Tabi Signed-off-by: Mark Brown --- arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig | 1 + arch/powerpc/configs/mpc85xx_defconfig | 1 + arch/powerpc/configs/mpc85xx_smp_defconfig | 1 + sound/soc/Kconfig | 1 - sound/soc/Makefile | 1 - sound/soc/fsl/Kconfig | 92 +++ sound/soc/fsl/Makefile | 22 + sound/soc/fsl/eukrea-tlv320.c | 164 ++++++ sound/soc/fsl/imx-audmux.c | 314 +++++++++++ sound/soc/fsl/imx-audmux.h | 60 ++ sound/soc/fsl/imx-pcm-dma-mx2.c | 175 ++++++ sound/soc/fsl/imx-pcm-fiq.c | 336 +++++++++++ sound/soc/fsl/imx-pcm.c | 105 ++++ sound/soc/fsl/imx-pcm.h | 32 ++ sound/soc/fsl/imx-ssi.c | 690 +++++++++++++++++++++++ sound/soc/fsl/imx-ssi.h | 216 +++++++ sound/soc/fsl/mx27vis-aic32x4.c | 245 ++++++++ sound/soc/fsl/phycore-ac97.c | 125 ++++ sound/soc/fsl/wm1133-ev1.c | 304 ++++++++++ sound/soc/imx/Kconfig | 79 --- sound/soc/imx/Makefile | 22 - sound/soc/imx/eukrea-tlv320.c | 164 ------ sound/soc/imx/imx-audmux.c | 314 ----------- sound/soc/imx/imx-audmux.h | 60 -- sound/soc/imx/imx-pcm-dma-mx2.c | 175 ------ sound/soc/imx/imx-pcm-fiq.c | 336 ----------- sound/soc/imx/imx-pcm.c | 105 ---- sound/soc/imx/imx-pcm.h | 32 -- sound/soc/imx/imx-ssi.c | 690 ----------------------- sound/soc/imx/imx-ssi.h | 216 ------- sound/soc/imx/mx27vis-aic32x4.c | 245 -------- sound/soc/imx/phycore-ac97.c | 125 ---- sound/soc/imx/wm1133-ev1.c | 304 ---------- 33 files changed, 2883 insertions(+), 2869 deletions(-) create mode 100644 sound/soc/fsl/eukrea-tlv320.c create mode 100644 sound/soc/fsl/imx-audmux.c create mode 100644 sound/soc/fsl/imx-audmux.h create mode 100644 sound/soc/fsl/imx-pcm-dma-mx2.c create mode 100644 sound/soc/fsl/imx-pcm-fiq.c create mode 100644 sound/soc/fsl/imx-pcm.c create mode 100644 sound/soc/fsl/imx-pcm.h create mode 100644 sound/soc/fsl/imx-ssi.c create mode 100644 sound/soc/fsl/imx-ssi.h create mode 100644 sound/soc/fsl/mx27vis-aic32x4.c create mode 100644 sound/soc/fsl/phycore-ac97.c create mode 100644 sound/soc/fsl/wm1133-ev1.c delete mode 100644 sound/soc/imx/Kconfig delete mode 100644 sound/soc/imx/Makefile delete mode 100644 sound/soc/imx/eukrea-tlv320.c delete mode 100644 sound/soc/imx/imx-audmux.c delete mode 100644 sound/soc/imx/imx-audmux.h delete mode 100644 sound/soc/imx/imx-pcm-dma-mx2.c delete mode 100644 sound/soc/imx/imx-pcm-fiq.c delete mode 100644 sound/soc/imx/imx-pcm.c delete mode 100644 sound/soc/imx/imx-pcm.h delete mode 100644 sound/soc/imx/imx-ssi.c delete mode 100644 sound/soc/imx/imx-ssi.h delete mode 100644 sound/soc/imx/mx27vis-aic32x4.c delete mode 100644 sound/soc/imx/phycore-ac97.c delete mode 100644 sound/soc/imx/wm1133-ev1.c (limited to 'arch') diff --git a/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig b/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig index 0db9ba0423ff..c09598b31de1 100644 --- a/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig +++ b/arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig @@ -100,6 +100,7 @@ CONFIG_SND_MIXER_OSS=y CONFIG_SND_PCM_OSS=y # CONFIG_SND_SUPPORT_OLD_API is not set CONFIG_SND_SOC=y +CONFIG_SND_POWERPC_SOC=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_CMOS=y CONFIG_EXT2_FS=y diff --git a/arch/powerpc/configs/mpc85xx_defconfig b/arch/powerpc/configs/mpc85xx_defconfig index cc87a8441566..b7ac92a8b28f 100644 --- a/arch/powerpc/configs/mpc85xx_defconfig +++ b/arch/powerpc/configs/mpc85xx_defconfig @@ -140,6 +140,7 @@ CONFIG_SND_INTEL8X0=y # CONFIG_SND_PPC is not set # CONFIG_SND_USB is not set CONFIG_SND_SOC=y +CONFIG_SND_POWERPC_SOC=y CONFIG_HID_A4TECH=y CONFIG_HID_APPLE=y CONFIG_HID_BELKIN=y diff --git a/arch/powerpc/configs/mpc85xx_smp_defconfig b/arch/powerpc/configs/mpc85xx_smp_defconfig index 48d6682f2434..41d5e4709a08 100644 --- a/arch/powerpc/configs/mpc85xx_smp_defconfig +++ b/arch/powerpc/configs/mpc85xx_smp_defconfig @@ -142,6 +142,7 @@ CONFIG_SND_INTEL8X0=y # CONFIG_SND_PPC is not set # CONFIG_SND_USB is not set CONFIG_SND_SOC=y +CONFIG_SND_POWERPC_SOC=y CONFIG_HID_A4TECH=y CONFIG_HID_APPLE=y CONFIG_HID_BELKIN=y diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 91c985599d32..0f85f6d526e0 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -35,7 +35,6 @@ source "sound/soc/blackfin/Kconfig" source "sound/soc/davinci/Kconfig" source "sound/soc/ep93xx/Kconfig" source "sound/soc/fsl/Kconfig" -source "sound/soc/imx/Kconfig" source "sound/soc/jz4740/Kconfig" source "sound/soc/nuc900/Kconfig" source "sound/soc/omap/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 2feaf376e94b..363dfd6cffe7 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -12,7 +12,6 @@ obj-$(CONFIG_SND_SOC) += blackfin/ obj-$(CONFIG_SND_SOC) += davinci/ obj-$(CONFIG_SND_SOC) += ep93xx/ obj-$(CONFIG_SND_SOC) += fsl/ -obj-$(CONFIG_SND_SOC) += imx/ obj-$(CONFIG_SND_SOC) += jz4740/ obj-$(CONFIG_SND_SOC) += mid-x86/ obj-$(CONFIG_SND_SOC) += mxs/ diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig index ca693b2ed838..19856a05208c 100644 --- a/sound/soc/fsl/Kconfig +++ b/sound/soc/fsl/Kconfig @@ -1,3 +1,15 @@ +config SND_SOC_FSL_SSI + tristate + +menuconfig SND_POWERPC_SOC + tristate "SoC Audio for Freescale PowerPC CPUs" + depends on FSL_SOC + help + Say Y or M if you want to add support for codecs attached to + the PowerPC CPUs. + +if SND_POWERPC_SOC + config SND_MPC52xx_DMA tristate @@ -68,3 +80,83 @@ config SND_MPC52xx_SOC_EFIKA help Say Y if you want to add support for sound on the Efika. +endif # SND_POWERPC_SOC + +menuconfig SND_IMX_SOC + tristate "SoC Audio for Freescale i.MX CPUs" + depends on ARCH_MXC + help + Say Y or M if you want to add support for codecs attached to + the i.MX CPUs. + +if SND_IMX_SOC + +config SND_SOC_IMX_SSI + tristate + +config SND_SOC_IMX_PCM + tristate + +config SND_MXC_SOC_FIQ + tristate + select FIQ + select SND_SOC_IMX_PCM + +config SND_MXC_SOC_MX2 + tristate + select SND_SOC_DMAENGINE_PCM + select SND_SOC_IMX_PCM + +config SND_SOC_IMX_AUDMUX + tristate + +config SND_MXC_SOC_WM1133_EV1 + tristate "Audio on the the i.MX31ADS with WM1133-EV1 fitted" + depends on MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL + select SND_SOC_WM8350 + select SND_MXC_SOC_FIQ + select SND_SOC_IMX_AUDMUX + select SND_SOC_IMX_SSI + help + Enable support for audio on the i.MX31ADS with the WM1133-EV1 + PMIC board with WM8835x fitted. + +config SND_SOC_MX27VIS_AIC32X4 + tristate "SoC audio support for Visstrim M10 boards" + depends on MACH_IMX27_VISSTRIM_M10 && I2C + select SND_SOC_TLV320AIC32X4 + select SND_MXC_SOC_MX2 + select SND_SOC_IMX_AUDMUX + select SND_SOC_IMX_SSI + help + Say Y if you want to add support for SoC audio on Visstrim SM10 + board with TLV320AIC32X4 codec. + +config SND_SOC_PHYCORE_AC97 + tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards" + depends on MACH_PCM043 || MACH_PCA100 + select SND_SOC_AC97_BUS + select SND_SOC_WM9712 + select SND_MXC_SOC_FIQ + select SND_SOC_IMX_AUDMUX + select SND_SOC_IMX_SSI + help + Say Y if you want to add support for SoC audio on Phytec phyCORE + and phyCARD boards in AC97 mode + +config SND_SOC_EUKREA_TLV320 + tristate "Eukrea TLV320" + depends on MACH_EUKREA_MBIMX27_BASEBOARD \ + || MACH_EUKREA_MBIMXSD25_BASEBOARD \ + || MACH_EUKREA_MBIMXSD35_BASEBOARD \ + || MACH_EUKREA_MBIMXSD51_BASEBOARD + depends on I2C + select SND_SOC_TLV320AIC23 + select SND_MXC_SOC_FIQ + select SND_SOC_IMX_AUDMUX + select SND_SOC_IMX_SSI + help + Enable I2S based access to the TLV320AIC23B codec attached + to the SSI interface + +endif # SND_IMX_SOC diff --git a/sound/soc/fsl/Makefile b/sound/soc/fsl/Makefile index 95d483f6af7f..36c257fd35bb 100644 --- a/sound/soc/fsl/Makefile +++ b/sound/soc/fsl/Makefile @@ -21,3 +21,25 @@ obj-$(CONFIG_SND_SOC_MPC5200_AC97) += mpc5200_psc_ac97.o obj-$(CONFIG_SND_MPC52xx_SOC_PCM030) += pcm030-audio-fabric.o obj-$(CONFIG_SND_MPC52xx_SOC_EFIKA) += efika-audio-fabric.o +# i.MX Platform Support +snd-soc-imx-ssi-objs := imx-ssi.o +snd-soc-imx-audmux-objs := imx-audmux.o + +obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o +obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o + +obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o +snd-soc-imx-pcm-y := imx-pcm.o +snd-soc-imx-pcm-$(CONFIG_SND_MXC_SOC_FIQ) += imx-pcm-fiq.o +snd-soc-imx-pcm-$(CONFIG_SND_MXC_SOC_MX2) += imx-pcm-dma-mx2.o + +# i.MX Machine Support +snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o +snd-soc-phycore-ac97-objs := phycore-ac97.o +snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o +snd-soc-wm1133-ev1-objs := wm1133-ev1.o + +obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o +obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o +obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o +obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o diff --git a/sound/soc/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c new file mode 100644 index 000000000000..efb9ede01208 --- /dev/null +++ b/sound/soc/fsl/eukrea-tlv320.c @@ -0,0 +1,164 @@ +/* + * eukrea-tlv320.c -- SoC audio for eukrea_cpuimxXX in I2S mode + * + * Copyright 2010 Eric Bénard, Eukréa Electromatique + * + * based on sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c + * which is Copyright 2009 Simtec Electronics + * and on sound/soc/imx/phycore-ac97.c which is + * Copyright 2009 Sascha Hauer, Pengutronix + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../codecs/tlv320aic23.h" +#include "imx-ssi.h" +#include "imx-audmux.h" + +#define CODEC_CLOCK 12000000 + +static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret; + + ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret) { + pr_err("%s: failed set cpu dai format\n", __func__); + return ret; + } + + ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM); + if (ret) { + pr_err("%s: failed set codec dai format\n", __func__); + return ret; + } + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, + CODEC_CLOCK, SND_SOC_CLOCK_OUT); + if (ret) { + pr_err("%s: failed setting codec sysclk\n", __func__); + return ret; + } + snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0); + + ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, + SND_SOC_CLOCK_IN); + if (ret) { + pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n"); + return ret; + } + + return 0; +} + +static struct snd_soc_ops eukrea_tlv320_snd_ops = { + .hw_params = eukrea_tlv320_hw_params, +}; + +static struct snd_soc_dai_link eukrea_tlv320_dai = { + .name = "tlv320aic23", + .stream_name = "TLV320AIC23", + .codec_dai_name = "tlv320aic23-hifi", + .platform_name = "imx-fiq-pcm-audio.0", + .codec_name = "tlv320aic23-codec.0-001a", + .cpu_dai_name = "imx-ssi.0", + .ops = &eukrea_tlv320_snd_ops, +}; + +static struct snd_soc_card eukrea_tlv320 = { + .name = "cpuimx-audio", + .owner = THIS_MODULE, + .dai_link = &eukrea_tlv320_dai, + .num_links = 1, +}; + +static struct platform_device *eukrea_tlv320_snd_device; + +static int __init eukrea_tlv320_init(void) +{ + int ret; + int int_port = 0, ext_port; + + if (machine_is_eukrea_cpuimx27()) { + imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, + IMX_AUDMUX_V1_PCR_SYN | + IMX_AUDMUX_V1_PCR_TFSDIR | + IMX_AUDMUX_V1_PCR_TCLKDIR | + IMX_AUDMUX_V1_PCR_RFSDIR | + IMX_AUDMUX_V1_PCR_RCLKDIR | + IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) | + IMX_AUDMUX_V1_PCR_RFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) | + IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) + ); + imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR3_SSI_PINS_4, + IMX_AUDMUX_V1_PCR_SYN | + IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) + ); + } else if (machine_is_eukrea_cpuimx25sd() || + machine_is_eukrea_cpuimx35sd() || + machine_is_eukrea_cpuimx51sd()) { + ext_port = machine_is_eukrea_cpuimx25sd() ? 4 : 3; + imx_audmux_v2_configure_port(int_port, + IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TFSDIR | + IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | + IMX_AUDMUX_V2_PTCR_TCLKDIR | + IMX_AUDMUX_V2_PTCR_TCSEL(ext_port), + IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port) + ); + imx_audmux_v2_configure_port(ext_port, + IMX_AUDMUX_V2_PTCR_SYN, + IMX_AUDMUX_V2_PDCR_RXDSEL(int_port) + ); + } else { + /* return happy. We might run on a totally different machine */ + return 0; + } + + eukrea_tlv320_snd_device = platform_device_alloc("soc-audio", -1); + if (!eukrea_tlv320_snd_device) + return -ENOMEM; + + platform_set_drvdata(eukrea_tlv320_snd_device, &eukrea_tlv320); + ret = platform_device_add(eukrea_tlv320_snd_device); + + if (ret) { + printk(KERN_ERR "ASoC: Platform device allocation failed\n"); + platform_device_put(eukrea_tlv320_snd_device); + } + + return ret; +} + +static void __exit eukrea_tlv320_exit(void) +{ + platform_device_unregister(eukrea_tlv320_snd_device); +} + +module_init(eukrea_tlv320_init); +module_exit(eukrea_tlv320_exit); + +MODULE_AUTHOR("Eric Bénard "); +MODULE_DESCRIPTION("CPUIMX ALSA SoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c new file mode 100644 index 000000000000..601df809a26a --- /dev/null +++ b/sound/soc/fsl/imx-audmux.c @@ -0,0 +1,314 @@ +/* + * Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2012 Linaro Ltd. + * Copyright 2009 Pengutronix, Sascha Hauer + * + * Initial development of this code was funded by + * Phytec Messtechnik GmbH, http://www.phytec.de + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "imx-audmux.h" + +#define DRIVER_NAME "imx-audmux" + +static struct clk *audmux_clk; +static void __iomem *audmux_base; + +#define IMX_AUDMUX_V2_PTCR(x) ((x) * 8) +#define IMX_AUDMUX_V2_PDCR(x) ((x) * 8 + 4) + +#ifdef CONFIG_DEBUG_FS +static struct dentry *audmux_debugfs_root; + +static int audmux_open_file(struct inode *inode, struct file *file) +{ + file->private_data = inode->i_private; + return 0; +} + +/* There is an annoying discontinuity in the SSI numbering with regard + * to the Linux number of the devices */ +static const char *audmux_port_string(int port) +{ + switch (port) { + case MX31_AUDMUX_PORT1_SSI0: + return "imx-ssi.0"; + case MX31_AUDMUX_PORT2_SSI1: + return "imx-ssi.1"; + case MX31_AUDMUX_PORT3_SSI_PINS_3: + return "SSI3"; + case MX31_AUDMUX_PORT4_SSI_PINS_4: + return "SSI4"; + case MX31_AUDMUX_PORT5_SSI_PINS_5: + return "SSI5"; + case MX31_AUDMUX_PORT6_SSI_PINS_6: + return "SSI6"; + default: + return "UNKNOWN"; + } +} + +static ssize_t audmux_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + ssize_t ret; + char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + int port = (int)file->private_data; + u32 pdcr, ptcr; + + if (!buf) + return -ENOMEM; + + if (audmux_clk) + clk_prepare_enable(audmux_clk); + + ptcr = readl(audmux_base + IMX_AUDMUX_V2_PTCR(port)); + pdcr = readl(audmux_base + IMX_AUDMUX_V2_PDCR(port)); + + if (audmux_clk) + clk_disable_unprepare(audmux_clk); + + ret = snprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n", + pdcr, ptcr); + + if (ptcr & IMX_AUDMUX_V2_PTCR_TFSDIR) + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "TxFS output from %s, ", + audmux_port_string((ptcr >> 27) & 0x7)); + else + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "TxFS input, "); + + if (ptcr & IMX_AUDMUX_V2_PTCR_TCLKDIR) + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "TxClk output from %s", + audmux_port_string((ptcr >> 22) & 0x7)); + else + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "TxClk input"); + + ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n"); + + if (ptcr & IMX_AUDMUX_V2_PTCR_SYN) { + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "Port is symmetric"); + } else { + if (ptcr & IMX_AUDMUX_V2_PTCR_RFSDIR) + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "RxFS output from %s, ", + audmux_port_string((ptcr >> 17) & 0x7)); + else + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "RxFS input, "); + + if (ptcr & IMX_AUDMUX_V2_PTCR_RCLKDIR) + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "RxClk output from %s", + audmux_port_string((ptcr >> 12) & 0x7)); + else + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "RxClk input"); + } + + ret += snprintf(buf + ret, PAGE_SIZE - ret, + "\nData received from %s\n", + audmux_port_string((pdcr >> 13) & 0x7)); + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + + kfree(buf); + + return ret; +} + +static const struct file_operations audmux_debugfs_fops = { + .open = audmux_open_file, + .read = audmux_read_file, + .llseek = default_llseek, +}; + +static void __init audmux_debugfs_init(void) +{ + int i; + char buf[20]; + + audmux_debugfs_root = debugfs_create_dir("audmux", NULL); + if (!audmux_debugfs_root) { + pr_warning("Failed to create AUDMUX debugfs root\n"); + return; + } + + for (i = 1; i < 8; i++) { + snprintf(buf, sizeof(buf), "ssi%d", i); + if (!debugfs_create_file(buf, 0444, audmux_debugfs_root, + (void *)i, &audmux_debugfs_fops)) + pr_warning("Failed to create AUDMUX port %d debugfs file\n", + i); + } +} + +static void __devexit audmux_debugfs_remove(void) +{ + debugfs_remove_recursive(audmux_debugfs_root); +} +#else +static inline void audmux_debugfs_init(void) +{ +} + +static inline void audmux_debugfs_remove(void) +{ +} +#endif + +enum imx_audmux_type { + IMX21_AUDMUX, + IMX31_AUDMUX, +} audmux_type; + +static struct platform_device_id imx_audmux_ids[] = { + { + .name = "imx21-audmux", + .driver_data = IMX21_AUDMUX, + }, { + .name = "imx31-audmux", + .driver_data = IMX31_AUDMUX, + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(platform, imx_audmux_ids); + +static const struct of_device_id imx_audmux_dt_ids[] = { + { .compatible = "fsl,imx21-audmux", .data = &imx_audmux_ids[0], }, + { .compatible = "fsl,imx31-audmux", .data = &imx_audmux_ids[1], }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_audmux_dt_ids); + +static const uint8_t port_mapping[] = { + 0x0, 0x4, 0x8, 0x10, 0x14, 0x1c, +}; + +int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr) +{ + if (audmux_type != IMX21_AUDMUX) + return -EINVAL; + + if (!audmux_base) + return -ENOSYS; + + if (port >= ARRAY_SIZE(port_mapping)) + return -EINVAL; + + writel(pcr, audmux_base + port_mapping[port]); + + return 0; +} +EXPORT_SYMBOL_GPL(imx_audmux_v1_configure_port); + +int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, + unsigned int pdcr) +{ + if (audmux_type != IMX31_AUDMUX) + return -EINVAL; + + if (!audmux_base) + return -ENOSYS; + + if (audmux_clk) + clk_prepare_enable(audmux_clk); + + writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port)); + writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port)); + + if (audmux_clk) + clk_disable_unprepare(audmux_clk); + + return 0; +} +EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port); + +static int __devinit imx_audmux_probe(struct platform_device *pdev) +{ + struct resource *res; + const struct of_device_id *of_id = + of_match_device(imx_audmux_dt_ids, &pdev->dev); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + audmux_base = devm_request_and_ioremap(&pdev->dev, res); + if (!audmux_base) + return -EADDRNOTAVAIL; + + audmux_clk = clk_get(&pdev->dev, "audmux"); + if (IS_ERR(audmux_clk)) { + dev_dbg(&pdev->dev, "cannot get clock: %ld\n", + PTR_ERR(audmux_clk)); + audmux_clk = NULL; + } + + if (of_id) + pdev->id_entry = of_id->data; + audmux_type = pdev->id_entry->driver_data; + if (audmux_type == IMX31_AUDMUX) + audmux_debugfs_init(); + + return 0; +} + +static int __devexit imx_audmux_remove(struct platform_device *pdev) +{ + if (audmux_type == IMX31_AUDMUX) + audmux_debugfs_remove(); + clk_put(audmux_clk); + + return 0; +} + +static struct platform_driver imx_audmux_driver = { + .probe = imx_audmux_probe, + .remove = __devexit_p(imx_audmux_remove), + .id_table = imx_audmux_ids, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + .of_match_table = imx_audmux_dt_ids, + } +}; + +static int __init imx_audmux_init(void) +{ + return platform_driver_register(&imx_audmux_driver); +} +subsys_initcall(imx_audmux_init); + +static void __exit imx_audmux_exit(void) +{ + platform_driver_unregister(&imx_audmux_driver); +} +module_exit(imx_audmux_exit); + +MODULE_DESCRIPTION("Freescale i.MX AUDMUX driver"); +MODULE_AUTHOR("Sascha Hauer "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/sound/soc/fsl/imx-audmux.h b/sound/soc/fsl/imx-audmux.h new file mode 100644 index 000000000000..04ebbab8d7b9 --- /dev/null +++ b/sound/soc/fsl/imx-audmux.h @@ -0,0 +1,60 @@ +#ifndef __IMX_AUDMUX_H +#define __IMX_AUDMUX_H + +#define MX27_AUDMUX_HPCR1_SSI0 0 +#define MX27_AUDMUX_HPCR2_SSI1 1 +#define MX27_AUDMUX_HPCR3_SSI_PINS_4 2 +#define MX27_AUDMUX_PPCR1_SSI_PINS_1 3 +#define MX27_AUDMUX_PPCR2_SSI_PINS_2 4 +#define MX27_AUDMUX_PPCR3_SSI_PINS_3 5 + +#define MX31_AUDMUX_PORT1_SSI0 0 +#define MX31_AUDMUX_PORT2_SSI1 1 +#define MX31_AUDMUX_PORT3_SSI_PINS_3 2 +#define MX31_AUDMUX_PORT4_SSI_PINS_4 3 +#define MX31_AUDMUX_PORT5_SSI_PINS_5 4 +#define MX31_AUDMUX_PORT6_SSI_PINS_6 5 + +#define MX51_AUDMUX_PORT1_SSI0 0 +#define MX51_AUDMUX_PORT2_SSI1 1 +#define MX51_AUDMUX_PORT3 2 +#define MX51_AUDMUX_PORT4 3 +#define MX51_AUDMUX_PORT5 4 +#define MX51_AUDMUX_PORT6 5 +#define MX51_AUDMUX_PORT7 6 + +/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */ +#define IMX_AUDMUX_V1_PCR_INMMASK(x) ((x) & 0xff) +#define IMX_AUDMUX_V1_PCR_INMEN (1 << 8) +#define IMX_AUDMUX_V1_PCR_TXRXEN (1 << 10) +#define IMX_AUDMUX_V1_PCR_SYN (1 << 12) +#define IMX_AUDMUX_V1_PCR_RXDSEL(x) (((x) & 0x7) << 13) +#define IMX_AUDMUX_V1_PCR_RFCSEL(x) (((x) & 0xf) << 20) +#define IMX_AUDMUX_V1_PCR_RCLKDIR (1 << 24) +#define IMX_AUDMUX_V1_PCR_RFSDIR (1 << 25) +#define IMX_AUDMUX_V1_PCR_TFCSEL(x) (((x) & 0xf) << 26) +#define IMX_AUDMUX_V1_PCR_TCLKDIR (1 << 30) +#define IMX_AUDMUX_V1_PCR_TFSDIR (1 << 31) + +/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */ +#define IMX_AUDMUX_V2_PTCR_TFSDIR (1 << 31) +#define IMX_AUDMUX_V2_PTCR_TFSEL(x) (((x) & 0xf) << 27) +#define IMX_AUDMUX_V2_PTCR_TCLKDIR (1 << 26) +#define IMX_AUDMUX_V2_PTCR_TCSEL(x) (((x) & 0xf) << 22) +#define IMX_AUDMUX_V2_PTCR_RFSDIR (1 << 21) +#define IMX_AUDMUX_V2_PTCR_RFSEL(x) (((x) & 0xf) << 17) +#define IMX_AUDMUX_V2_PTCR_RCLKDIR (1 << 16) +#define IMX_AUDMUX_V2_PTCR_RCSEL(x) (((x) & 0xf) << 12) +#define IMX_AUDMUX_V2_PTCR_SYN (1 << 11) + +#define IMX_AUDMUX_V2_PDCR_RXDSEL(x) (((x) & 0x7) << 13) +#define IMX_AUDMUX_V2_PDCR_TXRXEN (1 << 12) +#define IMX_AUDMUX_V2_PDCR_MODE(x) (((x) & 0x3) << 8) +#define IMX_AUDMUX_V2_PDCR_INMMASK(x) ((x) & 0xff) + +int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr); + +int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, + unsigned int pdcr); + +#endif /* __IMX_AUDMUX_H */ diff --git a/sound/soc/fsl/imx-pcm-dma-mx2.c b/sound/soc/fsl/imx-pcm-dma-mx2.c new file mode 100644 index 000000000000..6b818de2fc03 --- /dev/null +++ b/sound/soc/fsl/imx-pcm-dma-mx2.c @@ -0,0 +1,175 @@ +/* + * imx-pcm-dma-mx2.c -- ALSA Soc Audio Layer + * + * Copyright 2009 Sascha Hauer + * + * This code is based on code copyrighted by Freescale, + * Liam Girdwood, Javier Martin and probably others. + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "imx-pcm.h" + +static bool filter(struct dma_chan *chan, void *param) +{ + if (!imx_dma_is_general_purpose(chan)) + return false; + + chan->private = param; + + return true; +} + +static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); + struct imx_pcm_dma_params *dma_params; + struct dma_slave_config slave_config; + int ret; + + dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config); + if (ret) + return ret; + + slave_config.device_fc = false; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + slave_config.dst_addr = dma_params->dma_addr; + slave_config.dst_maxburst = dma_params->burstsize; + } else { + slave_config.src_addr = dma_params->dma_addr; + slave_config.src_maxburst = dma_params->burstsize; + } + + ret = dmaengine_slave_config(chan, &slave_config); + if (ret) + return ret; + + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static struct snd_pcm_hardware snd_imx_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, + .period_bytes_min = 128, + .period_bytes_max = 65535, /* Limited by SDMA engine */ + .periods_min = 2, + .periods_max = 255, + .fifo_size = 0, +}; + +static int snd_imx_open(struct snd_pcm_substream *substream) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct imx_pcm_dma_params *dma_params; + struct imx_dma_data *dma_data; + int ret; + + snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); + + dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); + + dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL); + dma_data->peripheral_type = IMX_DMATYPE_SSI; + dma_data->priority = DMA_PRIO_HIGH; + dma_data->dma_request = dma_params->dma; + + ret = snd_dmaengine_pcm_open(substream, filter, dma_data); + if (ret) { + kfree(dma_data); + return 0; + } + + snd_dmaengine_pcm_set_data(substream, dma_data); + + return 0; +} + +static int snd_imx_close(struct snd_pcm_substream *substream) +{ + struct imx_dma_data *dma_data = snd_dmaengine_pcm_get_data(substream); + + snd_dmaengine_pcm_close(substream); + kfree(dma_data); + + return 0; +} + +static struct snd_pcm_ops imx_pcm_ops = { + .open = snd_imx_open, + .close = snd_imx_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_imx_pcm_hw_params, + .trigger = snd_dmaengine_pcm_trigger, + .pointer = snd_dmaengine_pcm_pointer, + .mmap = snd_imx_pcm_mmap, +}; + +static struct snd_soc_platform_driver imx_soc_platform_mx2 = { + .ops = &imx_pcm_ops, + .pcm_new = imx_pcm_new, + .pcm_free = imx_pcm_free, +}; + +static int __devinit imx_soc_platform_probe(struct platform_device *pdev) +{ + return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2); +} + +static int __devexit imx_soc_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static struct platform_driver imx_pcm_driver = { + .driver = { + .name = "imx-pcm-audio", + .owner = THIS_MODULE, + }, + .probe = imx_soc_platform_probe, + .remove = __devexit_p(imx_soc_platform_remove), +}; + +module_platform_driver(imx_pcm_driver); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:imx-pcm-audio"); diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c new file mode 100644 index 000000000000..456b7d723d66 --- /dev/null +++ b/sound/soc/fsl/imx-pcm-fiq.c @@ -0,0 +1,336 @@ +/* + * imx-pcm-fiq.c -- ALSA Soc Audio Layer + * + * Copyright 2009 Sascha Hauer + * + * This code is based on code copyrighted by Freescale, + * Liam Girdwood, Javier Martin and probably others. + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include "imx-ssi.h" + +struct imx_pcm_runtime_data { + int period; + int periods; + unsigned long offset; + unsigned long last_offset; + unsigned long size; + struct hrtimer hrt; + int poll_time_ns; + struct snd_pcm_substream *substream; + atomic_t running; +}; + +static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) +{ + struct imx_pcm_runtime_data *iprtd = + container_of(hrt, struct imx_pcm_runtime_data, hrt); + struct snd_pcm_substream *substream = iprtd->substream; + struct snd_pcm_runtime *runtime = substream->runtime; + struct pt_regs regs; + unsigned long delta; + + if (!atomic_read(&iprtd->running)) + return HRTIMER_NORESTART; + + get_fiq_regs(®s); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + iprtd->offset = regs.ARM_r8 & 0xffff; + else + iprtd->offset = regs.ARM_r9 & 0xffff; + + /* How much data have we transferred since the last period report? */ + if (iprtd->offset >= iprtd->last_offset) + delta = iprtd->offset - iprtd->last_offset; + else + delta = runtime->buffer_size + iprtd->offset + - iprtd->last_offset; + + /* If we've transferred at least a period then report it and + * reset our poll time */ + if (delta >= iprtd->period) { + snd_pcm_period_elapsed(substream); + iprtd->last_offset = iprtd->offset; + } + + hrtimer_forward_now(hrt, ns_to_ktime(iprtd->poll_time_ns)); + + return HRTIMER_RESTART; +} + +static struct fiq_handler fh = { + .name = DRV_NAME, +}; + +static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct imx_pcm_runtime_data *iprtd = runtime->private_data; + + iprtd->size = params_buffer_bytes(params); + iprtd->periods = params_periods(params); + iprtd->period = params_period_bytes(params) ; + iprtd->offset = 0; + iprtd->last_offset = 0; + iprtd->poll_time_ns = 1000000000 / params_rate(params) * + params_period_size(params); + snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); + + return 0; +} + +static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct imx_pcm_runtime_data *iprtd = runtime->private_data; + struct pt_regs regs; + + get_fiq_regs(®s); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + regs.ARM_r8 = (iprtd->period * iprtd->periods - 1) << 16; + else + regs.ARM_r9 = (iprtd->period * iprtd->periods - 1) << 16; + + set_fiq_regs(®s); + + return 0; +} + +static int fiq_enable; +static int imx_pcm_fiq; + +static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct imx_pcm_runtime_data *iprtd = runtime->private_data; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + atomic_set(&iprtd->running, 1); + hrtimer_start(&iprtd->hrt, ns_to_ktime(iprtd->poll_time_ns), + HRTIMER_MODE_REL); + if (++fiq_enable == 1) + enable_fiq(imx_pcm_fiq); + + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + atomic_set(&iprtd->running, 0); + + if (--fiq_enable == 0) + disable_fiq(imx_pcm_fiq); + + break; + default: + return -EINVAL; + } + + return 0; +} + +static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct imx_pcm_runtime_data *iprtd = runtime->private_data; + + return bytes_to_frames(substream->runtime, iprtd->offset); +} + +static struct snd_pcm_hardware snd_imx_hardware = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_RESUME, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rate_min = 8000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, + .period_bytes_min = 128, + .period_bytes_max = 16 * 1024, + .periods_min = 4, + .periods_max = 255, + .fifo_size = 0, +}; + +static int snd_imx_open(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct imx_pcm_runtime_data *iprtd; + int ret; + + iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL); + if (iprtd == NULL) + return -ENOMEM; + runtime->private_data = iprtd; + + iprtd->substream = substream; + + atomic_set(&iprtd->running, 0); + hrtimer_init(&iprtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + iprtd->hrt.function = snd_hrtimer_callback; + + ret = snd_pcm_hw_constraint_integer(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + if (ret < 0) { + kfree(iprtd); + return ret; + } + + snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); + return 0; +} + +static int snd_imx_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct imx_pcm_runtime_data *iprtd = runtime->private_data; + + hrtimer_cancel(&iprtd->hrt); + + kfree(iprtd); + + return 0; +} + +static struct snd_pcm_ops imx_pcm_ops = { + .open = snd_imx_open, + .close = snd_imx_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_imx_pcm_hw_params, + .prepare = snd_imx_pcm_prepare, + .trigger = snd_imx_pcm_trigger, + .pointer = snd_imx_pcm_pointer, + .mmap = snd_imx_pcm_mmap, +}; + +static int ssi_irq = 0; + +static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_pcm *pcm = rtd->pcm; + struct snd_pcm_substream *substream; + int ret; + + ret = imx_pcm_new(rtd); + if (ret) + return ret; + + substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; + if (substream) { + struct snd_dma_buffer *buf = &substream->dma_buffer; + + imx_ssi_fiq_tx_buffer = (unsigned long)buf->area; + } + + substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + if (substream) { + struct snd_dma_buffer *buf = &substream->dma_buffer; + + imx_ssi_fiq_rx_buffer = (unsigned long)buf->area; + } + + set_fiq_handler(&imx_ssi_fiq_start, + &imx_ssi_fiq_end - &imx_ssi_fiq_start); + + return 0; +} + +static void imx_pcm_fiq_free(struct snd_pcm *pcm) +{ + mxc_set_irq_fiq(ssi_irq, 0); + release_fiq(&fh); + imx_pcm_free(pcm); +} + +static struct snd_soc_platform_driver imx_soc_platform_fiq = { + .ops = &imx_pcm_ops, + .pcm_new = imx_pcm_fiq_new, + .pcm_free = imx_pcm_fiq_free, +}; + +static int __devinit imx_soc_platform_probe(struct platform_device *pdev) +{ + struct imx_ssi *ssi = platform_get_drvdata(pdev); + int ret; + + ret = claim_fiq(&fh); + if (ret) { + dev_err(&pdev->dev, "failed to claim fiq: %d", ret); + return ret; + } + + mxc_set_irq_fiq(ssi->irq, 1); + ssi_irq = ssi->irq; + + imx_pcm_fiq = ssi->irq; + + imx_ssi_fiq_base = (unsigned long)ssi->base; + + ssi->dma_params_tx.burstsize = 4; + ssi->dma_params_rx.burstsize = 6; + + ret = snd_soc_register_platform(&pdev->dev, &imx_soc_platform_fiq); + if (ret) + goto failed_register; + + return 0; + +failed_register: + mxc_set_irq_fiq(ssi_irq, 0); + release_fiq(&fh); + + return ret; +} + +static int __devexit imx_soc_platform_remove(struct platform_device *pdev) +{ + snd_soc_unregister_platform(&pdev->dev); + return 0; +} + +static struct platform_driver imx_pcm_driver = { + .driver = { + .name = "imx-fiq-pcm-audio", + .owner = THIS_MODULE, + }, + + .probe = imx_soc_platform_probe, + .remove = __devexit_p(imx_soc_platform_remove), +}; + +module_platform_driver(imx_pcm_driver); + +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/imx-pcm.c b/sound/soc/fsl/imx-pcm.c new file mode 100644 index 000000000000..93dc360b1777 --- /dev/null +++ b/sound/soc/fsl/imx-pcm.c @@ -0,0 +1,105 @@ +/* + * Copyright 2009 Sascha Hauer + * + * This code is based on code copyrighted by Freescale, + * Liam Girdwood, Javier Martin and probably others. + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include "imx-pcm.h" + +int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int ret; + + ret = dma_mmap_writecombine(substream->pcm->card->dev, vma, + runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); + + pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret, + runtime->dma_area, + runtime->dma_addr, + runtime->dma_bytes); + return ret; +} +EXPORT_SYMBOL_GPL(snd_imx_pcm_mmap); + +static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +{ + struct snd_pcm_substream *substream = pcm->streams[stream].substream; + struct snd_dma_buffer *buf = &substream->dma_buffer; + size_t size = IMX_SSI_DMABUF_SIZE; + + buf->dev.type = SNDRV_DMA_TYPE_DEV; + buf->dev.dev = pcm->card->dev; + buf->private_data = NULL; + buf->area = dma_alloc_writecombine(pcm->card->dev, size, + &buf->addr, GFP_KERNEL); + if (!buf->area) + return -ENOMEM; + buf->bytes = size; + + return 0; +} + +static u64 imx_pcm_dmamask = DMA_BIT_MASK(32); + +int imx_pcm_new(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_card *card = rtd->card->snd_card; + struct snd_pcm *pcm = rtd->pcm; + int ret = 0; + + if (!card->dev->dma_mask) + card->dev->dma_mask = &imx_pcm_dmamask; + if (!card->dev->coherent_dma_mask) + card->dev->coherent_dma_mask = DMA_BIT_MASK(32); + if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { + ret = imx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_PLAYBACK); + if (ret) + goto out; + } + + if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { + ret = imx_pcm_preallocate_dma_buffer(pcm, + SNDRV_PCM_STREAM_CAPTURE); + if (ret) + goto out; + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(imx_pcm_new); + +void imx_pcm_free(struct snd_pcm *pcm) +{ + struct snd_pcm_substream *substream; + struct snd_dma_buffer *buf; + int stream; + + for (stream = 0; stream < 2; stream++) { + substream = pcm->streams[stream].substream; + if (!substream) + continue; + + buf = &substream->dma_buffer; + if (!buf->area) + continue; + + dma_free_writecombine(pcm->card->dev, buf->bytes, + buf->area, buf->addr); + buf->area = NULL; + } +} +EXPORT_SYMBOL_GPL(imx_pcm_free); diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h new file mode 100644 index 000000000000..b5f5c3acf34d --- /dev/null +++ b/sound/soc/fsl/imx-pcm.h @@ -0,0 +1,32 @@ +/* + * Copyright 2009 Sascha Hauer + * + * This code is based on code copyrighted by Freescale, + * Liam Girdwood, Javier Martin and probably others. + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ + +#ifndef _IMX_PCM_H +#define _IMX_PCM_H + +/* + * Do not change this as the FIQ handler depends on this size + */ +#define IMX_SSI_DMABUF_SIZE (64 * 1024) + +struct imx_pcm_dma_params { + int dma; + unsigned long dma_addr; + int burstsize; +}; + +int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, + struct vm_area_struct *vma); +int imx_pcm_new(struct snd_soc_pcm_runtime *rtd); +void imx_pcm_free(struct snd_pcm *pcm); + +#endif /* _IMX_PCM_H */ diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c new file mode 100644 index 000000000000..cf3ed0362c9c --- /dev/null +++ b/sound/soc/fsl/imx-ssi.c @@ -0,0 +1,690 @@ +/* + * imx-ssi.c -- ALSA Soc Audio Layer + * + * Copyright 2009 Sascha Hauer + * + * This code is based on code copyrighted by Freescale, + * Liam Girdwood, Javier Martin and probably others. + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + * + * The i.MX SSI core has some nasty limitations in AC97 mode. While most + * sane processor vendors have a FIFO per AC97 slot, the i.MX has only + * one FIFO which combines all valid receive slots. We cannot even select + * which slots we want to receive. The WM9712 with which this driver + * was developed with always sends GPIO status data in slot 12 which + * we receive in our (PCM-) data stream. The only chance we have is to + * manually skip this data in the FIQ handler. With sampling rates different + * from 48000Hz not every frame has valid receive data, so the ratio + * between pcm data and GPIO status data changes. Our FIQ handler is not + * able to handle this, hence this driver only works with 48000Hz sampling + * rate. + * Reading and writing AC97 registers is another challenge. The core + * provides us status bits when the read register is updated with *another* + * value. When we read the same register two times (and the register still + * contains the same value) these status bits are not set. We work + * around this by not polling these bits but only wait a fixed delay. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "imx-ssi.h" + +#define SSI_SACNT_DEFAULT (SSI_SACNT_AC97EN | SSI_SACNT_FV) + +/* + * SSI Network Mode or TDM slots configuration. + * Should only be called when port is inactive (i.e. SSIEN = 0). + */ +static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) +{ + struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); + u32 sccr; + + sccr = readl(ssi->base + SSI_STCCR); + sccr &= ~SSI_STCCR_DC_MASK; + sccr |= SSI_STCCR_DC(slots - 1); + writel(sccr, ssi->base + SSI_STCCR); + + sccr = readl(ssi->base + SSI_SRCCR); + sccr &= ~SSI_STCCR_DC_MASK; + sccr |= SSI_STCCR_DC(slots - 1); + writel(sccr, ssi->base + SSI_SRCCR); + + writel(tx_mask, ssi->base + SSI_STMSK); + writel(rx_mask, ssi->base + SSI_SRMSK); + + return 0; +} + +/* + * SSI DAI format configuration. + * Should only be called when port is inactive (i.e. SSIEN = 0). + */ +static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) +{ + struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); + u32 strcr = 0, scr; + + scr = readl(ssi->base + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET); + + /* DAI mode */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + /* data on rising edge of bclk, frame low 1clk before data */ + strcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0; + scr |= SSI_SCR_NET; + if (ssi->flags & IMX_SSI_USE_I2S_SLAVE) { + scr &= ~SSI_I2S_MODE_MASK; + scr |= SSI_SCR_I2S_MODE_SLAVE; + } + break; + case SND_SOC_DAIFMT_LEFT_J: + /* data on rising edge of bclk, frame high with data */ + strcr |= SSI_STCR_TXBIT0; + break; + case SND_SOC_DAIFMT_DSP_B: + /* data on rising edge of bclk, frame high with data */ + strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0; + break; + case SND_SOC_DAIFMT_DSP_A: + /* data on rising edge of bclk, frame high 1clk before data */ + strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0 | SSI_STCR_TEFS; + break; + } + + /* DAI clock inversion */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_IF: + strcr |= SSI_STCR_TFSI; + strcr &= ~SSI_STCR_TSCKP; + break; + case SND_SOC_DAIFMT_IB_NF: + strcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI); + break; + case SND_SOC_DAIFMT_NB_IF: + strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP; + break; + case SND_SOC_DAIFMT_NB_NF: + strcr &= ~SSI_STCR_TFSI; + strcr |= SSI_STCR_TSCKP; + break; + } + + /* DAI clock master masks */ + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + break; + default: + /* Master mode not implemented, needs handling of clocks. */ + return -EINVAL; + } + + strcr |= SSI_STCR_TFEN0; + + if (ssi->flags & IMX_SSI_NET) + scr |= SSI_SCR_NET; + if (ssi->flags & IMX_SSI_SYN) + scr |= SSI_SCR_SYN; + + writel(strcr, ssi->base + SSI_STCR); + writel(strcr, ssi->base + SSI_SRCR); + writel(scr, ssi->base + SSI_SCR); + + return 0; +} + +/* + * SSI system clock configuration. + * Should only be called when port is inactive (i.e. SSIEN = 0). + */ +static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, + int clk_id, unsigned int freq, int dir) +{ + struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); + u32 scr; + + scr = readl(ssi->base + SSI_SCR); + + switch (clk_id) { + case IMX_SSP_SYS_CLK: + if (dir == SND_SOC_CLOCK_OUT) + scr |= SSI_SCR_SYS_CLK_EN; + else + scr &= ~SSI_SCR_SYS_CLK_EN; + break; + default: + return -EINVAL; + } + + writel(scr, ssi->base + SSI_SCR); + + return 0; +} + +/* + * SSI Clock dividers + * Should only be called when port is inactive (i.e. SSIEN = 0). + */ +static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, + int div_id, int div) +{ + struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); + u32 stccr, srccr; + + stccr = readl(ssi->base + SSI_STCCR); + srccr = readl(ssi->base + SSI_SRCCR); + + switch (div_id) { + case IMX_SSI_TX_DIV_2: + stccr &= ~SSI_STCCR_DIV2; + stccr |= div; + break; + case IMX_SSI_TX_DIV_PSR: + stccr &= ~SSI_STCCR_PSR; + stccr |= div; + break; + case IMX_SSI_TX_DIV_PM: + stccr &= ~0xff; + stccr |= SSI_STCCR_PM(div); + break; + case IMX_SSI_RX_DIV_2: + stccr &= ~SSI_STCCR_DIV2; + stccr |= div; + break; + case IMX_SSI_RX_DIV_PSR: + stccr &= ~SSI_STCCR_PSR; + stccr |= div; + break; + case IMX_SSI_RX_DIV_PM: + stccr &= ~0xff; + stccr |= SSI_STCCR_PM(div); + break; + default: + return -EINVAL; + } + + writel(stccr, ssi->base + SSI_STCCR); + writel(srccr, ssi->base + SSI_SRCCR); + + return 0; +} + +static int imx_ssi_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); + struct imx_pcm_dma_params *dma_data; + + /* Tx/Rx config */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + dma_data = &ssi->dma_params_tx; + else + dma_data = &ssi->dma_params_rx; + + snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); + + return 0; +} + +/* + * Should only be called when port is inactive (i.e. SSIEN = 0), + * although can be called multiple times by upper layers. + */ +static int imx_ssi_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *cpu_dai) +{ + struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); + u32 reg, sccr; + + /* Tx/Rx config */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + reg = SSI_STCCR; + else + reg = SSI_SRCCR; + + if (ssi->flags & IMX_SSI_SYN) + reg = SSI_STCCR; + + sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK; + + /* DAI data (word) size */ + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + sccr |= SSI_SRCCR_WL(16); + break; + case SNDRV_PCM_FORMAT_S20_3LE: + sccr |= SSI_SRCCR_WL(20); + break; + case SNDRV_PCM_FORMAT_S24_LE: + sccr |= SSI_SRCCR_WL(24); + break; + } + + writel(sccr, ssi->base + reg); + + return 0; +} + +static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) +{ + struct imx_ssi *ssi = snd_soc_dai_get_drvdata(dai); + unsigned int sier_bits, sier; + unsigned int scr; + + scr = readl(ssi->base + SSI_SCR); + sier = readl(ssi->base + SSI_SIER); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + if (ssi->flags & IMX_SSI_DMA) + sier_bits = SSI_SIER_TDMAE; + else + sier_bits = SSI_SIER_TIE | SSI_SIER_TFE0_EN; + } else { + if (ssi->flags & IMX_SSI_DMA) + sier_bits = SSI_SIER_RDMAE; + else + sier_bits = SSI_SIER_RIE | SSI_SIER_RFF0_EN; + } + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + scr |= SSI_SCR_TE; + else + scr |= SSI_SCR_RE; + sier |= sier_bits; + + if (++ssi->enabled == 1) + scr |= SSI_SCR_SSIEN; + + break; + + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + scr &= ~SSI_SCR_TE; + else + scr &= ~SSI_SCR_RE; + sier &= ~sier_bits; + + if (--ssi->enabled == 0) + scr &= ~SSI_SCR_SSIEN; + + break; + default: + return -EINVAL; + } + + if (!(ssi->flags & IMX_SSI_USE_AC97)) + /* rx/tx are always enabled to access ac97 registers */ + writel(scr, ssi->base + SSI_SCR); + + writel(sier, ssi->base + SSI_SIER); + + return 0; +} + +static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { + .startup = imx_ssi_startup, + .hw_params = imx_ssi_hw_params, + .set_fmt = imx_ssi_set_dai_fmt, + .set_clkdiv = imx_ssi_set_dai_clkdiv, + .set_sysclk = imx_ssi_set_dai_sysclk, + .set_tdm_slot = imx_ssi_set_dai_tdm_slot, + .trigger = imx_ssi_trigger, +}; + +static int imx_ssi_dai_probe(struct snd_soc_dai *dai) +{ + struct imx_ssi *ssi = dev_get_drvdata(dai->dev); + uint32_t val; + + snd_soc_dai_set_drvdata(dai, ssi); + + val = SSI_SFCSR_TFWM0(ssi->dma_params_tx.burstsize) | + SSI_SFCSR_RFWM0(ssi->dma_params_rx.burstsize); + writel(val, ssi->base + SSI_SFCSR); + + return 0; +} + +static struct snd_soc_dai_driver imx_ssi_dai = { + .probe = imx_ssi_dai_probe, + .playback = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &imx_ssi_pcm_dai_ops, +}; + +static struct snd_soc_dai_driver imx_ac97_dai = { + .probe = imx_ssi_dai_probe, + .ac97_control = 1, + .playback = { + .stream_name = "AC97 Playback", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .capture = { + .stream_name = "AC97 Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE, + }, + .ops = &imx_ssi_pcm_dai_ops, +}; + +static void setup_channel_to_ac97(struct imx_ssi *imx_ssi) +{ + void __iomem *base = imx_ssi->base; + + writel(0x0, base + SSI_SCR); + writel(0x0, base + SSI_STCR); + writel(0x0, base + SSI_SRCR); + + writel(SSI_SCR_SYN | SSI_SCR_NET, base + SSI_SCR); + + writel(SSI_SFCSR_RFWM0(8) | + SSI_SFCSR_TFWM0(8) | + SSI_SFCSR_RFWM1(8) | + SSI_SFCSR_TFWM1(8), base + SSI_SFCSR); + + writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_STCCR); + writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_SRCCR); + + writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN, base + SSI_SCR); + writel(SSI_SOR_WAIT(3), base + SSI_SOR); + + writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN | + SSI_SCR_TE | SSI_SCR_RE, + base + SSI_SCR); + + writel(SSI_SACNT_DEFAULT, base + SSI_SACNT); + writel(0xff, base + SSI_SACCDIS); + writel(0x300, base + SSI_SACCEN); +} + +static struct imx_ssi *ac97_ssi; + +static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + struct imx_ssi *imx_ssi = ac97_ssi; + void __iomem *base = imx_ssi->base; + unsigned int lreg; + unsigned int lval; + + if (reg > 0x7f) + return; + + pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); + + lreg = reg << 12; + writel(lreg, base + SSI_SACADD); + + lval = val << 4; + writel(lval , base + SSI_SACDAT); + + writel(SSI_SACNT_DEFAULT | SSI_SACNT_WR, base + SSI_SACNT); + udelay(100); +} + +static unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + struct imx_ssi *imx_ssi = ac97_ssi; + void __iomem *base = imx_ssi->base; + + unsigned short val = -1; + unsigned int lreg; + + lreg = (reg & 0x7f) << 12 ; + writel(lreg, base + SSI_SACADD); + writel(SSI_SACNT_DEFAULT | SSI_SACNT_RD, base + SSI_SACNT); + + udelay(100); + + val = (readl(base + SSI_SACDAT) >> 4) & 0xffff; + + pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); + + return val; +} + +static void imx_ssi_ac97_reset(struct snd_ac97 *ac97) +{ + struct imx_ssi *imx_ssi = ac97_ssi; + + if (imx_ssi->ac97_reset) + imx_ssi->ac97_reset(ac97); +} + +static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97) +{ + struct imx_ssi *imx_ssi = ac97_ssi; + + if (imx_ssi->ac97_warm_reset) + imx_ssi->ac97_warm_reset(ac97); +} + +struct snd_ac97_bus_ops soc_ac97_ops = { + .read = imx_ssi_ac97_read, + .write = imx_ssi_ac97_write, + .reset = imx_ssi_ac97_reset, + .warm_reset = imx_ssi_ac97_warm_reset +}; +EXPORT_SYMBOL_GPL(soc_ac97_ops); + +static int imx_ssi_probe(struct platform_device *pdev) +{ + struct resource *res; + struct imx_ssi *ssi; + struct imx_ssi_platform_data *pdata = pdev->dev.platform_data; + int ret = 0; + struct snd_soc_dai_driver *dai; + + ssi = kzalloc(sizeof(*ssi), GFP_KERNEL); + if (!ssi) + return -ENOMEM; + dev_set_drvdata(&pdev->dev, ssi); + + if (pdata) { + ssi->ac97_reset = pdata->ac97_reset; + ssi->ac97_warm_reset = pdata->ac97_warm_reset; + ssi->flags = pdata->flags; + } + + ssi->irq = platform_get_irq(pdev, 0); + + ssi->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(ssi->clk)) { + ret = PTR_ERR(ssi->clk); + dev_err(&pdev->dev, "Cannot get the clock: %d\n", + ret); + goto failed_clk; + } + clk_enable(ssi->clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + goto failed_get_resource; + } + + if (!request_mem_region(res->start, resource_size(res), DRV_NAME)) { + dev_err(&pdev->dev, "request_mem_region failed\n"); + ret = -EBUSY; + goto failed_get_resource; + } + + ssi->base = ioremap(res->start, resource_size(res)); + if (!ssi->base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -ENODEV; + goto failed_ioremap; + } + + if (ssi->flags & IMX_SSI_USE_AC97) { + if (ac97_ssi) { + ret = -EBUSY; + goto failed_ac97; + } + ac97_ssi = ssi; + setup_channel_to_ac97(ssi); + dai = &imx_ac97_dai; + } else + dai = &imx_ssi_dai; + + writel(0x0, ssi->base + SSI_SIER); + + ssi->dma_params_rx.dma_addr = res->start + SSI_SRX0; + ssi->dma_params_tx.dma_addr = res->start + SSI_STX0; + + ssi->dma_params_tx.burstsize = 6; + ssi->dma_params_rx.burstsize = 4; + + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0"); + if (res) + ssi->dma_params_tx.dma = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0"); + if (res) + ssi->dma_params_rx.dma = res->start; + + platform_set_drvdata(pdev, ssi); + + ret = snd_soc_register_dai(&pdev->dev, dai); + if (ret) { + dev_err(&pdev->dev, "register DAI failed\n"); + goto failed_register; + } + + ssi->soc_platform_pdev_fiq = platform_device_alloc("imx-fiq-pcm-audio", pdev->id); + if (!ssi->soc_platform_pdev_fiq) { + ret = -ENOMEM; + goto failed_pdev_fiq_alloc; + } + + platform_set_drvdata(ssi->soc_platform_pdev_fiq, ssi); + ret = platform_device_add(ssi->soc_platform_pdev_fiq); + if (ret) { + dev_err(&pdev->dev, "failed to add platform device\n"); + goto failed_pdev_fiq_add; + } + + ssi->soc_platform_pdev = platform_device_alloc("imx-pcm-audio", pdev->id); + if (!ssi->soc_platform_pdev) { + ret = -ENOMEM; + goto failed_pdev_alloc; + } + + platform_set_drvdata(ssi->soc_platform_pdev, ssi); + ret = platform_device_add(ssi->soc_platform_pdev); + if (ret) { + dev_err(&pdev->dev, "failed to add platform device\n"); + goto failed_pdev_add; + } + + return 0; + +failed_pdev_add: + platform_device_put(ssi->soc_platform_pdev); +failed_pdev_alloc: + platform_device_del(ssi->soc_platform_pdev_fiq); +failed_pdev_fiq_add: + platform_device_put(ssi->soc_platform_pdev_fiq); +failed_pdev_fiq_alloc: + snd_soc_unregister_dai(&pdev->dev); +failed_register: +failed_ac97: + iounmap(ssi->base); +failed_ioremap: + release_mem_region(res->start, resource_size(res)); +failed_get_resource: + clk_disable(ssi->clk); + clk_put(ssi->clk); +failed_clk: + kfree(ssi); + + return ret; +} + +static int __devexit imx_ssi_remove(struct platform_device *pdev) +{ + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct imx_ssi *ssi = platform_get_drvdata(pdev); + + platform_device_unregister(ssi->soc_platform_pdev); + platform_device_unregister(ssi->soc_platform_pdev_fiq); + + snd_soc_unregister_dai(&pdev->dev); + + if (ssi->flags & IMX_SSI_USE_AC97) + ac97_ssi = NULL; + + iounmap(ssi->base); + release_mem_region(res->start, resource_size(res)); + clk_disable(ssi->clk); + clk_put(ssi->clk); + kfree(ssi); + + return 0; +} + +static struct platform_driver imx_ssi_driver = { + .probe = imx_ssi_probe, + .remove = __devexit_p(imx_ssi_remove), + + .driver = { + .name = "imx-ssi", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(imx_ssi_driver); + +/* Module information */ +MODULE_AUTHOR("Sascha Hauer, "); +MODULE_DESCRIPTION("i.MX I2S/ac97 SoC Interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:imx-ssi"); diff --git a/sound/soc/fsl/imx-ssi.h b/sound/soc/fsl/imx-ssi.h new file mode 100644 index 000000000000..5744e86ca878 --- /dev/null +++ b/sound/soc/fsl/imx-ssi.h @@ -0,0 +1,216 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _IMX_SSI_H +#define _IMX_SSI_H + +#define SSI_STX0 0x00 +#define SSI_STX1 0x04 +#define SSI_SRX0 0x08 +#define SSI_SRX1 0x0c + +#define SSI_SCR 0x10 +#define SSI_SCR_CLK_IST (1 << 9) +#define SSI_SCR_CLK_IST_SHIFT 9 +#define SSI_SCR_TCH_EN (1 << 8) +#define SSI_SCR_SYS_CLK_EN (1 << 7) +#define SSI_SCR_I2S_MODE_NORM (0 << 5) +#define SSI_SCR_I2S_MODE_MSTR (1 << 5) +#define SSI_SCR_I2S_MODE_SLAVE (2 << 5) +#define SSI_I2S_MODE_MASK (3 << 5) +#define SSI_SCR_SYN (1 << 4) +#define SSI_SCR_NET (1 << 3) +#define SSI_SCR_RE (1 << 2) +#define SSI_SCR_TE (1 << 1) +#define SSI_SCR_SSIEN (1 << 0) + +#define SSI_SISR 0x14 +#define SSI_SISR_MASK ((1 << 19) - 1) +#define SSI_SISR_CMDAU (1 << 18) +#define SSI_SISR_CMDDU (1 << 17) +#define SSI_SISR_RXT (1 << 16) +#define SSI_SISR_RDR1 (1 << 15) +#define SSI_SISR_RDR0 (1 << 14) +#define SSI_SISR_TDE1 (1 << 13) +#define SSI_SISR_TDE0 (1 << 12) +#define SSI_SISR_ROE1 (1 << 11) +#define SSI_SISR_ROE0 (1 << 10) +#define SSI_SISR_TUE1 (1 << 9) +#define SSI_SISR_TUE0 (1 << 8) +#define SSI_SISR_TFS (1 << 7) +#define SSI_SISR_RFS (1 << 6) +#define SSI_SISR_TLS (1 << 5) +#define SSI_SISR_RLS (1 << 4) +#define SSI_SISR_RFF1 (1 << 3) +#define SSI_SISR_RFF0 (1 << 2) +#define SSI_SISR_TFE1 (1 << 1) +#define SSI_SISR_TFE0 (1 << 0) + +#define SSI_SIER 0x18 +#define SSI_SIER_RDMAE (1 << 22) +#define SSI_SIER_RIE (1 << 21) +#define SSI_SIER_TDMAE (1 << 20) +#define SSI_SIER_TIE (1 << 19) +#define SSI_SIER_CMDAU_EN (1 << 18) +#define SSI_SIER_CMDDU_EN (1 << 17) +#define SSI_SIER_RXT_EN (1 << 16) +#define SSI_SIER_RDR1_EN (1 << 15) +#define SSI_SIER_RDR0_EN (1 << 14) +#define SSI_SIER_TDE1_EN (1 << 13) +#define SSI_SIER_TDE0_EN (1 << 12) +#define SSI_SIER_ROE1_EN (1 << 11) +#define SSI_SIER_ROE0_EN (1 << 10) +#define SSI_SIER_TUE1_EN (1 << 9) +#define SSI_SIER_TUE0_EN (1 << 8) +#define SSI_SIER_TFS_EN (1 << 7) +#define SSI_SIER_RFS_EN (1 << 6) +#define SSI_SIER_TLS_EN (1 << 5) +#define SSI_SIER_RLS_EN (1 << 4) +#define SSI_SIER_RFF1_EN (1 << 3) +#define SSI_SIER_RFF0_EN (1 << 2) +#define SSI_SIER_TFE1_EN (1 << 1) +#define SSI_SIER_TFE0_EN (1 << 0) + +#define SSI_STCR 0x1c +#define SSI_STCR_TXBIT0 (1 << 9) +#define SSI_STCR_TFEN1 (1 << 8) +#define SSI_STCR_TFEN0 (1 << 7) +#define SSI_FIFO_ENABLE_0_SHIFT 7 +#define SSI_STCR_TFDIR (1 << 6) +#define SSI_STCR_TXDIR (1 << 5) +#define SSI_STCR_TSHFD (1 << 4) +#define SSI_STCR_TSCKP (1 << 3) +#define SSI_STCR_TFSI (1 << 2) +#define SSI_STCR_TFSL (1 << 1) +#define SSI_STCR_TEFS (1 << 0) + +#define SSI_SRCR 0x20 +#define SSI_SRCR_RXBIT0 (1 << 9) +#define SSI_SRCR_RFEN1 (1 << 8) +#define SSI_SRCR_RFEN0 (1 << 7) +#define SSI_FIFO_ENABLE_0_SHIFT 7 +#define SSI_SRCR_RFDIR (1 << 6) +#define SSI_SRCR_RXDIR (1 << 5) +#define SSI_SRCR_RSHFD (1 << 4) +#define SSI_SRCR_RSCKP (1 << 3) +#define SSI_SRCR_RFSI (1 << 2) +#define SSI_SRCR_RFSL (1 << 1) +#define SSI_SRCR_REFS (1 << 0) + +#define SSI_SRCCR 0x28 +#define SSI_SRCCR_DIV2 (1 << 18) +#define SSI_SRCCR_PSR (1 << 17) +#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13) +#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8) +#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0) +#define SSI_SRCCR_WL_MASK (0xf << 13) +#define SSI_SRCCR_DC_MASK (0x1f << 8) +#define SSI_SRCCR_PM_MASK (0xff << 0) + +#define SSI_STCCR 0x24 +#define SSI_STCCR_DIV2 (1 << 18) +#define SSI_STCCR_PSR (1 << 17) +#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13) +#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8) +#define SSI_STCCR_PM(x) (((x) & 0xff) << 0) +#define SSI_STCCR_WL_MASK (0xf << 13) +#define SSI_STCCR_DC_MASK (0x1f << 8) +#define SSI_STCCR_PM_MASK (0xff << 0) + +#define SSI_SFCSR 0x2c +#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28) +#define SSI_RX_FIFO_1_COUNT_SHIFT 28 +#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24) +#define SSI_TX_FIFO_1_COUNT_SHIFT 24 +#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20) +#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16) +#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12) +#define SSI_RX_FIFO_0_COUNT_SHIFT 12 +#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8) +#define SSI_TX_FIFO_0_COUNT_SHIFT 8 +#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4) +#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0) +#define SSI_SFCSR_RFWM0_MASK (0xf << 4) +#define SSI_SFCSR_TFWM0_MASK (0xf << 0) + +#define SSI_STR 0x30 +#define SSI_STR_TEST (1 << 15) +#define SSI_STR_RCK2TCK (1 << 14) +#define SSI_STR_RFS2TFS (1 << 13) +#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8) +#define SSI_STR_TXD2RXD (1 << 7) +#define SSI_STR_TCK2RCK (1 << 6) +#define SSI_STR_TFS2RFS (1 << 5) +#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0) + +#define SSI_SOR 0x34 +#define SSI_SOR_CLKOFF (1 << 6) +#define SSI_SOR_RX_CLR (1 << 5) +#define SSI_SOR_TX_CLR (1 << 4) +#define SSI_SOR_INIT (1 << 3) +#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1) +#define SSI_SOR_WAIT_MASK (0x3 << 1) +#define SSI_SOR_SYNRST (1 << 0) + +#define SSI_SACNT 0x38 +#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5) +#define SSI_SACNT_WR (1 << 4) +#define SSI_SACNT_RD (1 << 3) +#define SSI_SACNT_TIF (1 << 2) +#define SSI_SACNT_FV (1 << 1) +#define SSI_SACNT_AC97EN (1 << 0) + +#define SSI_SACADD 0x3c +#define SSI_SACDAT 0x40 +#define SSI_SATAG 0x44 +#define SSI_STMSK 0x48 +#define SSI_SRMSK 0x4c +#define SSI_SACCST 0x50 +#define SSI_SACCEN 0x54 +#define SSI_SACCDIS 0x58 + +/* SSI clock sources */ +#define IMX_SSP_SYS_CLK 0 + +/* SSI audio dividers */ +#define IMX_SSI_TX_DIV_2 0 +#define IMX_SSI_TX_DIV_PSR 1 +#define IMX_SSI_TX_DIV_PM 2 +#define IMX_SSI_RX_DIV_2 3 +#define IMX_SSI_RX_DIV_PSR 4 +#define IMX_SSI_RX_DIV_PM 5 + +#define DRV_NAME "imx-ssi" + +#include +#include +#include "imx-pcm.h" + +struct imx_ssi { + struct platform_device *ac97_dev; + + struct snd_soc_dai *imx_ac97; + struct clk *clk; + void __iomem *base; + int irq; + int fiq_enable; + unsigned int offset; + + unsigned int flags; + + void (*ac97_reset) (struct snd_ac97 *ac97); + void (*ac97_warm_reset)(struct snd_ac97 *ac97); + + struct imx_pcm_dma_params dma_params_rx; + struct imx_pcm_dma_params dma_params_tx; + + int enabled; + + struct platform_device *soc_platform_pdev; + struct platform_device *soc_platform_pdev_fiq; +}; + +#endif /* _IMX_SSI_H */ diff --git a/sound/soc/fsl/mx27vis-aic32x4.c b/sound/soc/fsl/mx27vis-aic32x4.c new file mode 100644 index 000000000000..f6d04ad4bb39 --- /dev/null +++ b/sound/soc/fsl/mx27vis-aic32x4.c @@ -0,0 +1,245 @@ +/* + * mx27vis-aic32x4.c + * + * Copyright 2011 Vista Silicon S.L. + * + * Author: Javier Martin + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../codecs/tlv320aic32x4.h" +#include "imx-ssi.h" +#include "imx-audmux.h" + +#define MX27VIS_AMP_GAIN 0 +#define MX27VIS_AMP_MUTE 1 + +#define MX27VIS_PIN_G0 (GPIO_PORTF + 9) +#define MX27VIS_PIN_G1 (GPIO_PORTF + 8) +#define MX27VIS_PIN_SDL (GPIO_PORTE + 5) +#define MX27VIS_PIN_SDR (GPIO_PORTF + 7) + +static int mx27vis_amp_gain; +static int mx27vis_amp_mute; + +static const int mx27vis_amp_pins[] = { + MX27VIS_PIN_G0 | GPIO_GPIO | GPIO_OUT, + MX27VIS_PIN_G1 | GPIO_GPIO | GPIO_OUT, + MX27VIS_PIN_SDL | GPIO_GPIO | GPIO_OUT, + MX27VIS_PIN_SDR | GPIO_GPIO | GPIO_OUT, +}; + +static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int ret; + u32 dai_format; + + dai_format = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + + /* set codec DAI configuration */ + snd_soc_dai_set_fmt(codec_dai, dai_format); + + /* set cpu DAI configuration */ + snd_soc_dai_set_fmt(cpu_dai, dai_format); + + ret = snd_soc_dai_set_sysclk(codec_dai, 0, + 25000000, SND_SOC_CLOCK_OUT); + if (ret) { + pr_err("%s: failed setting codec sysclk\n", __func__); + return ret; + } + + ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, + SND_SOC_CLOCK_IN); + if (ret) { + pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n"); + return ret; + } + + return 0; +} + +static struct snd_soc_ops mx27vis_aic32x4_snd_ops = { + .hw_params = mx27vis_aic32x4_hw_params, +}; + +static int mx27vis_amp_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + int value = ucontrol->value.integer.value[0]; + unsigned int reg = mc->reg; + int max = mc->max; + + if (value > max) + return -EINVAL; + + switch (reg) { + case MX27VIS_AMP_GAIN: + gpio_set_value(MX27VIS_PIN_G0, value & 1); + gpio_set_value(MX27VIS_PIN_G1, value >> 1); + mx27vis_amp_gain = value; + break; + case MX27VIS_AMP_MUTE: + gpio_set_value(MX27VIS_PIN_SDL, value & 1); + gpio_set_value(MX27VIS_PIN_SDR, value >> 1); + mx27vis_amp_mute = value; + break; + } + return 0; +} + +static int mx27vis_amp_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int reg = mc->reg; + + switch (reg) { + case MX27VIS_AMP_GAIN: + ucontrol->value.integer.value[0] = mx27vis_amp_gain; + break; + case MX27VIS_AMP_MUTE: + ucontrol->value.integer.value[0] = mx27vis_amp_mute; + break; + } + return 0; +} + +/* From 6dB to 24dB in steps of 6dB */ +static const DECLARE_TLV_DB_SCALE(mx27vis_amp_tlv, 600, 600, 0); + +static const struct snd_kcontrol_new mx27vis_aic32x4_controls[] = { + SOC_DAPM_PIN_SWITCH("External Mic"), + SOC_SINGLE_EXT_TLV("LO Ext Boost", MX27VIS_AMP_GAIN, 0, 3, 0, + mx27vis_amp_get, mx27vis_amp_set, mx27vis_amp_tlv), + SOC_DOUBLE_EXT("LO Ext Mute Switch", MX27VIS_AMP_MUTE, 0, 1, 1, 0, + mx27vis_amp_get, mx27vis_amp_set), +}; + +static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { + SND_SOC_DAPM_MIC("External Mic", NULL), +}; + +static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { + {"Mic Bias", NULL, "External Mic"}, + {"IN1_R", NULL, "Mic Bias"}, + {"IN2_R", NULL, "Mic Bias"}, + {"IN3_R", NULL, "Mic Bias"}, + {"IN1_L", NULL, "Mic Bias"}, + {"IN2_L", NULL, "Mic Bias"}, + {"IN3_L", NULL, "Mic Bias"}, +}; + +static struct snd_soc_dai_link mx27vis_aic32x4_dai = { + .name = "tlv320aic32x4", + .stream_name = "TLV320AIC32X4", + .codec_dai_name = "tlv320aic32x4-hifi", + .platform_name = "imx-pcm-audio.0", + .codec_name = "tlv320aic32x4.0-0018", + .cpu_dai_name = "imx-ssi.0", + .ops = &mx27vis_aic32x4_snd_ops, +}; + +static struct snd_soc_card mx27vis_aic32x4 = { + .name = "visstrim_m10-audio", + .owner = THIS_MODULE, + .dai_link = &mx27vis_aic32x4_dai, + .num_links = 1, + .controls = mx27vis_aic32x4_controls, + .num_controls = ARRAY_SIZE(mx27vis_aic32x4_controls), + .dapm_widgets = aic32x4_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets), + .dapm_routes = aic32x4_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes), +}; + +static int __devinit mx27vis_aic32x4_probe(struct platform_device *pdev) +{ + int ret; + + mx27vis_aic32x4.dev = &pdev->dev; + ret = snd_soc_register_card(&mx27vis_aic32x4); + if (ret) { + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", + ret); + return ret; + } + + /* Connect SSI0 as clock slave to SSI1 external pins */ + imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, + IMX_AUDMUX_V1_PCR_SYN | + IMX_AUDMUX_V1_PCR_TFSDIR | + IMX_AUDMUX_V1_PCR_TCLKDIR | + IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) | + IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) + ); + imx_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1, + IMX_AUDMUX_V1_PCR_SYN | + IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) + ); + + ret = mxc_gpio_setup_multiple_pins(mx27vis_amp_pins, + ARRAY_SIZE(mx27vis_amp_pins), "MX27VIS_AMP"); + if (ret) + printk(KERN_ERR "ASoC: unable to setup gpios\n"); + + return ret; +} + +static int __devexit mx27vis_aic32x4_remove(struct platform_device *pdev) +{ + snd_soc_unregister_card(&mx27vis_aic32x4); + + return 0; +} + +static struct platform_driver mx27vis_aic32x4_audio_driver = { + .driver = { + .name = "mx27vis", + .owner = THIS_MODULE, + }, + .probe = mx27vis_aic32x4_probe, + .remove = __devexit_p(mx27vis_aic32x4_remove), +}; + +module_platform_driver(mx27vis_aic32x4_audio_driver); + +MODULE_AUTHOR("Javier Martin "); +MODULE_DESCRIPTION("ALSA SoC AIC32X4 mx27 visstrim"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:mx27vis"); diff --git a/sound/soc/fsl/phycore-ac97.c b/sound/soc/fsl/phycore-ac97.c new file mode 100644 index 000000000000..f8da6dd115ed --- /dev/null +++ b/sound/soc/fsl/phycore-ac97.c @@ -0,0 +1,125 @@ +/* + * phycore-ac97.c -- SoC audio for imx_phycore in AC97 mode + * + * Copyright 2009 Sascha Hauer, Pengutronix + * + * 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; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "imx-audmux.h" + +static struct snd_soc_card imx_phycore; + +static struct snd_soc_ops imx_phycore_hifi_ops = { +}; + +static struct snd_soc_dai_link imx_phycore_dai_ac97[] = { + { + .name = "HiFi", + .stream_name = "HiFi", + .codec_dai_name = "wm9712-hifi", + .codec_name = "wm9712-codec", + .cpu_dai_name = "imx-ssi.0", + .platform_name = "imx-fiq-pcm-audio.0", + .ops = &imx_phycore_hifi_ops, + }, +}; + +static struct snd_soc_card imx_phycore = { + .name = "PhyCORE-ac97-audio", + .owner = THIS_MODULE, + .dai_link = imx_phycore_dai_ac97, + .num_links = ARRAY_SIZE(imx_phycore_dai_ac97), +}; + +static struct platform_device *imx_phycore_snd_ac97_device; +static struct platform_device *imx_phycore_snd_device; + +static int __init imx_phycore_init(void) +{ + int ret; + + if (machine_is_pca100()) { + imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, + IMX_AUDMUX_V1_PCR_SYN | /* 4wire mode */ + IMX_AUDMUX_V1_PCR_TFCSEL(3) | + IMX_AUDMUX_V1_PCR_TCLKDIR | /* clock is output */ + IMX_AUDMUX_V1_PCR_RXDSEL(3)); + imx_audmux_v1_configure_port(3, + IMX_AUDMUX_V1_PCR_SYN | /* 4wire mode */ + IMX_AUDMUX_V1_PCR_TFCSEL(0) | + IMX_AUDMUX_V1_PCR_TFSDIR | + IMX_AUDMUX_V1_PCR_RXDSEL(0)); + } else if (machine_is_pcm043()) { + imx_audmux_v2_configure_port(3, + IMX_AUDMUX_V2_PTCR_SYN | /* 4wire mode */ + IMX_AUDMUX_V2_PTCR_TFSEL(0) | + IMX_AUDMUX_V2_PTCR_TFSDIR, + IMX_AUDMUX_V2_PDCR_RXDSEL(0)); + imx_audmux_v2_configure_port(0, + IMX_AUDMUX_V2_PTCR_SYN | /* 4wire mode */ + IMX_AUDMUX_V2_PTCR_TCSEL(3) | + IMX_AUDMUX_V2_PTCR_TCLKDIR, /* clock is output */ + IMX_AUDMUX_V2_PDCR_RXDSEL(3)); + } else { + /* return happy. We might run on a totally different machine */ + return 0; + } + + imx_phycore_snd_ac97_device = platform_device_alloc("soc-audio", -1); + if (!imx_phycore_snd_ac97_device) + return -ENOMEM; + + platform_set_drvdata(imx_phycore_snd_ac97_device, &imx_phycore); + ret = platform_device_add(imx_phycore_snd_ac97_device); + if (ret) + goto fail1; + + imx_phycore_snd_device = platform_device_alloc("wm9712-codec", -1); + if (!imx_phycore_snd_device) { + ret = -ENOMEM; + goto fail2; + } + ret = platform_device_add(imx_phycore_snd_device); + + if (ret) { + printk(KERN_ERR "ASoC: Platform device allocation failed\n"); + goto fail3; + } + + return 0; + +fail3: + platform_device_put(imx_phycore_snd_device); +fail2: + platform_device_del(imx_phycore_snd_ac97_device); +fail1: + platform_device_put(imx_phycore_snd_ac97_device); + return ret; +} + +static void __exit imx_phycore_exit(void) +{ + platform_device_unregister(imx_phycore_snd_device); + platform_device_unregister(imx_phycore_snd_ac97_device); +} + +late_initcall(imx_phycore_init); +module_exit(imx_phycore_exit); + +MODULE_AUTHOR("Sascha Hauer "); +MODULE_DESCRIPTION("PhyCORE ALSA SoC driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c new file mode 100644 index 000000000000..fe54a69073e5 --- /dev/null +++ b/sound/soc/fsl/wm1133-ev1.c @@ -0,0 +1,304 @@ +/* + * wm1133-ev1.c - Audio for WM1133-EV1 on i.MX31ADS + * + * Copyright (c) 2010 Wolfson Microelectronics plc + * Author: Mark Brown + * + * Based on an earlier driver for the same hardware by Liam Girdwood. + * + * 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; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "imx-ssi.h" +#include "../codecs/wm8350.h" +#include "imx-audmux.h" + +/* There is a silicon mic on the board optionally connected via a solder pad + * SP1. Define this to enable it. + */ +#undef USE_SIMIC + +struct _wm8350_audio { + unsigned int channels; + snd_pcm_format_t format; + unsigned int rate; + unsigned int sysclk; + unsigned int bclkdiv; + unsigned int clkdiv; + unsigned int lr_rate; +}; + +/* in order of power consumption per rate (lowest first) */ +static const struct _wm8350_audio wm8350_audio[] = { + /* 16bit mono modes */ + {1, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000 >> 1, + WM8350_BCLK_DIV_48, WM8350_DACDIV_3, 16,}, + + /* 16 bit stereo modes */ + {2, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000, + WM8350_BCLK_DIV_48, WM8350_DACDIV_6, 32,}, + {2, SNDRV_PCM_FORMAT_S16_LE, 16000, 12288000, + WM8350_BCLK_DIV_24, WM8350_DACDIV_3, 32,}, + {2, SNDRV_PCM_FORMAT_S16_LE, 32000, 12288000, + WM8350_BCLK_DIV_12, WM8350_DACDIV_1_5, 32,}, + {2, SNDRV_PCM_FORMAT_S16_LE, 48000, 12288000, + WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,}, + {2, SNDRV_PCM_FORMAT_S16_LE, 96000, 24576000, + WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,}, + {2, SNDRV_PCM_FORMAT_S16_LE, 11025, 11289600, + WM8350_BCLK_DIV_32, WM8350_DACDIV_4, 32,}, + {2, SNDRV_PCM_FORMAT_S16_LE, 22050, 11289600, + WM8350_BCLK_DIV_16, WM8350_DACDIV_2, 32,}, + {2, SNDRV_PCM_FORMAT_S16_LE, 44100, 11289600, + WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,}, + {2, SNDRV_PCM_FORMAT_S16_LE, 88200, 22579200, + WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,}, + + /* 24bit stereo modes */ + {2, SNDRV_PCM_FORMAT_S24_LE, 48000, 12288000, + WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,}, + {2, SNDRV_PCM_FORMAT_S24_LE, 96000, 24576000, + WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,}, + {2, SNDRV_PCM_FORMAT_S24_LE, 44100, 11289600, + WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,}, + {2, SNDRV_PCM_FORMAT_S24_LE, 88200, 22579200, + WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,}, +}; + +static int wm1133_ev1_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + int i, found = 0; + snd_pcm_format_t format = params_format(params); + unsigned int rate = params_rate(params); + unsigned int channels = params_channels(params); + u32 dai_format; + + /* find the correct audio parameters */ + for (i = 0; i < ARRAY_SIZE(wm8350_audio); i++) { + if (rate == wm8350_audio[i].rate && + format == wm8350_audio[i].format && + channels == wm8350_audio[i].channels) { + found = 1; + break; + } + } + if (!found) + return -EINVAL; + + /* codec FLL input is 14.75 MHz from MCLK */ + snd_soc_dai_set_pll(codec_dai, 0, 0, 14750000, wm8350_audio[i].sysclk); + + dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBM_CFM; + + /* set codec DAI configuration */ + snd_soc_dai_set_fmt(codec_dai, dai_format); + + /* set cpu DAI configuration */ + snd_soc_dai_set_fmt(cpu_dai, dai_format); + + /* TODO: The SSI driver should figure this out for us */ + switch (channels) { + case 2: + snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0); + break; + case 1: + snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffe, 0xffffffe, 1, 0); + break; + default: + return -EINVAL; + } + + /* set MCLK as the codec system clock for DAC and ADC */ + snd_soc_dai_set_sysclk(codec_dai, WM8350_MCLK_SEL_PLL_MCLK, + wm8350_audio[i].sysclk, SND_SOC_CLOCK_IN); + + /* set codec BCLK division for sample rate */ + snd_soc_dai_set_clkdiv(codec_dai, WM8350_BCLK_CLKDIV, + wm8350_audio[i].bclkdiv); + + /* DAI is synchronous and clocked with DAC LRCLK & ADC LRC */ + snd_soc_dai_set_clkdiv(codec_dai, + WM8350_DACLR_CLKDIV, wm8350_audio[i].lr_rate); + snd_soc_dai_set_clkdiv(codec_dai, + WM8350_ADCLR_CLKDIV, wm8350_audio[i].lr_rate); + + /* now configure DAC and ADC clocks */ + snd_soc_dai_set_clkdiv(codec_dai, + WM8350_DAC_CLKDIV, wm8350_audio[i].clkdiv); + + snd_soc_dai_set_clkdiv(codec_dai, + WM8350_ADC_CLKDIV, wm8350_audio[i].clkdiv); + + return 0; +} + +static struct snd_soc_ops wm1133_ev1_ops = { + .hw_params = wm1133_ev1_hw_params, +}; + +static const struct snd_soc_dapm_widget wm1133_ev1_widgets[] = { +#ifdef USE_SIMIC + SND_SOC_DAPM_MIC("SiMIC", NULL), +#endif + SND_SOC_DAPM_MIC("Mic1 Jack", NULL), + SND_SOC_DAPM_MIC("Mic2 Jack", NULL), + SND_SOC_DAPM_LINE("Line In Jack", NULL), + SND_SOC_DAPM_LINE("Line Out Jack", NULL), + SND_SOC_DAPM_HP("Headphone Jack", NULL), +}; + +/* imx32ads soc_card audio map */ +static const struct snd_soc_dapm_route wm1133_ev1_map[] = { + +#ifdef USE_SIMIC + /* SiMIC --> IN1LN (with automatic bias) via SP1 */ + { "IN1LN", NULL, "Mic Bias" }, + { "Mic Bias", NULL, "SiMIC" }, +#endif + + /* Mic 1 Jack --> IN1LN and IN1LP (with automatic bias) */ + { "IN1LN", NULL, "Mic Bias" }, + { "IN1LP", NULL, "Mic1 Jack" }, + { "Mic Bias", NULL, "Mic1 Jack" }, + + /* Mic 2 Jack --> IN1RN and IN1RP (with automatic bias) */ + { "IN1RN", NULL, "Mic Bias" }, + { "IN1RP", NULL, "Mic2 Jack" }, + { "Mic Bias", NULL, "Mic2 Jack" }, + + /* Line in Jack --> AUX (L+R) */ + { "IN3R", NULL, "Line In Jack" }, + { "IN3L", NULL, "Line In Jack" }, + + /* Out1 --> Headphone Jack */ + { "Headphone Jack", NULL, "OUT1R" }, + { "Headphone Jack", NULL, "OUT1L" }, + + /* Out1 --> Line Out Jack */ + { "Line Out Jack", NULL, "OUT2R" }, + { "Line Out Jack", NULL, "OUT2L" }, +}; + +static struct snd_soc_jack hp_jack; + +static struct snd_soc_jack_pin hp_jack_pins[] = { + { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE }, +}; + +static struct snd_soc_jack mic_jack; + +static struct snd_soc_jack_pin mic_jack_pins[] = { + { .pin = "Mic1 Jack", .mask = SND_JACK_MICROPHONE }, + { .pin = "Mic2 Jack", .mask = SND_JACK_MICROPHONE }, +}; + +static int wm1133_ev1_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_dapm_context *dapm = &codec->dapm; + + snd_soc_dapm_new_controls(dapm, wm1133_ev1_widgets, + ARRAY_SIZE(wm1133_ev1_widgets)); + + snd_soc_dapm_add_routes(dapm, wm1133_ev1_map, + ARRAY_SIZE(wm1133_ev1_map)); + + /* Headphone jack detection */ + snd_soc_jack_new(codec, "Headphone", SND_JACK_HEADPHONE, &hp_jack); + snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins), + hp_jack_pins); + wm8350_hp_jack_detect(codec, WM8350_JDR, &hp_jack, SND_JACK_HEADPHONE); + + /* Microphone jack detection */ + snd_soc_jack_new(codec, "Microphone", + SND_JACK_MICROPHONE | SND_JACK_BTN_0, &mic_jack); + snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins), + mic_jack_pins); + wm8350_mic_jack_detect(codec, &mic_jack, SND_JACK_MICROPHONE, + SND_JACK_BTN_0); + + snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); + + return 0; +} + + +static struct snd_soc_dai_link wm1133_ev1_dai = { + .name = "WM1133-EV1", + .stream_name = "Audio", + .cpu_dai_name = "imx-ssi.0", + .codec_dai_name = "wm8350-hifi", + .platform_name = "imx-fiq-pcm-audio.0", + .codec_name = "wm8350-codec.0-0x1a", + .init = wm1133_ev1_init, + .ops = &wm1133_ev1_ops, + .symmetric_rates = 1, +}; + +static struct snd_soc_card wm1133_ev1 = { + .name = "WM1133-EV1", + .owner = THIS_MODULE, + .dai_link = &wm1133_ev1_dai, + .num_links = 1, +}; + +static struct platform_device *wm1133_ev1_snd_device; + +static int __init wm1133_ev1_audio_init(void) +{ + int ret; + unsigned int ptcr, pdcr; + + /* SSI0 mastered by port 5 */ + ptcr = IMX_AUDMUX_V2_PTCR_SYN | + IMX_AUDMUX_V2_PTCR_TFSDIR | + IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT5_SSI_PINS_5) | + IMX_AUDMUX_V2_PTCR_TCLKDIR | + IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT5_SSI_PINS_5); + pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT5_SSI_PINS_5); + imx_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0, ptcr, pdcr); + + ptcr = IMX_AUDMUX_V2_PTCR_SYN; + pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0); + imx_audmux_v2_configure_port(MX31_AUDMUX_PORT5_SSI_PINS_5, ptcr, pdcr); + + wm1133_ev1_snd_device = platform_device_alloc("soc-audio", -1); + if (!wm1133_ev1_snd_device) + return -ENOMEM; + + platform_set_drvdata(wm1133_ev1_snd_device, &wm1133_ev1); + ret = platform_device_add(wm1133_ev1_snd_device); + + if (ret) + platform_device_put(wm1133_ev1_snd_device); + + return ret; +} +module_init(wm1133_ev1_audio_init); + +static void __exit wm1133_ev1_audio_exit(void) +{ + platform_device_unregister(wm1133_ev1_snd_device); +} +module_exit(wm1133_ev1_audio_exit); + +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("Audio for WM1133-EV1 on i.MX31ADS"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/imx/Kconfig b/sound/soc/imx/Kconfig deleted file mode 100644 index 810acaa09009..000000000000 --- a/sound/soc/imx/Kconfig +++ /dev/null @@ -1,79 +0,0 @@ -menuconfig SND_IMX_SOC - tristate "SoC Audio for Freescale i.MX CPUs" - depends on ARCH_MXC - help - Say Y or M if you want to add support for codecs attached to - the i.MX SSI interface. - - -if SND_IMX_SOC - -config SND_SOC_IMX_SSI - tristate - -config SND_SOC_IMX_PCM - tristate - -config SND_MXC_SOC_FIQ - tristate - select FIQ - select SND_SOC_IMX_PCM - -config SND_MXC_SOC_MX2 - select SND_SOC_DMAENGINE_PCM - tristate - select SND_SOC_IMX_PCM - -config SND_SOC_IMX_AUDMUX - tristate - -config SND_MXC_SOC_WM1133_EV1 - tristate "Audio on the the i.MX31ADS with WM1133-EV1 fitted" - depends on MACH_MX31ADS_WM1133_EV1 && EXPERIMENTAL - select SND_SOC_WM8350 - select SND_MXC_SOC_FIQ - select SND_SOC_IMX_AUDMUX - select SND_SOC_IMX_SSI - help - Enable support for audio on the i.MX31ADS with the WM1133-EV1 - PMIC board with WM8835x fitted. - -config SND_SOC_MX27VIS_AIC32X4 - tristate "SoC audio support for Visstrim M10 boards" - depends on MACH_IMX27_VISSTRIM_M10 && I2C - select SND_SOC_TLV320AIC32X4 - select SND_MXC_SOC_MX2 - select SND_SOC_IMX_AUDMUX - select SND_SOC_IMX_SSI - help - Say Y if you want to add support for SoC audio on Visstrim SM10 - board with TLV320AIC32X4 codec. - -config SND_SOC_PHYCORE_AC97 - tristate "SoC Audio support for Phytec phyCORE (and phyCARD) boards" - depends on MACH_PCM043 || MACH_PCA100 - select SND_SOC_AC97_BUS - select SND_SOC_WM9712 - select SND_MXC_SOC_FIQ - select SND_SOC_IMX_AUDMUX - select SND_SOC_IMX_SSI - help - Say Y if you want to add support for SoC audio on Phytec phyCORE - and phyCARD boards in AC97 mode - -config SND_SOC_EUKREA_TLV320 - tristate "Eukrea TLV320" - depends on MACH_EUKREA_MBIMX27_BASEBOARD \ - || MACH_EUKREA_MBIMXSD25_BASEBOARD \ - || MACH_EUKREA_MBIMXSD35_BASEBOARD \ - || MACH_EUKREA_MBIMXSD51_BASEBOARD - depends on I2C - select SND_SOC_TLV320AIC23 - select SND_MXC_SOC_FIQ - select SND_SOC_IMX_AUDMUX - select SND_SOC_IMX_SSI - help - Enable I2S based access to the TLV320AIC23B codec attached - to the SSI interface - -endif # SND_IMX_SOC diff --git a/sound/soc/imx/Makefile b/sound/soc/imx/Makefile deleted file mode 100644 index f5db3e92d0d1..000000000000 --- a/sound/soc/imx/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -# i.MX Platform Support -snd-soc-imx-ssi-objs := imx-ssi.o -snd-soc-imx-audmux-objs := imx-audmux.o - -obj-$(CONFIG_SND_SOC_IMX_SSI) += snd-soc-imx-ssi.o -obj-$(CONFIG_SND_SOC_IMX_AUDMUX) += snd-soc-imx-audmux.o - -obj-$(CONFIG_SND_SOC_IMX_PCM) += snd-soc-imx-pcm.o -snd-soc-imx-pcm-y := imx-pcm.o -snd-soc-imx-pcm-$(CONFIG_SND_MXC_SOC_FIQ) += imx-pcm-fiq.o -snd-soc-imx-pcm-$(CONFIG_SND_MXC_SOC_MX2) += imx-pcm-dma-mx2.o - -# i.MX Machine Support -snd-soc-eukrea-tlv320-objs := eukrea-tlv320.o -snd-soc-phycore-ac97-objs := phycore-ac97.o -snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o -snd-soc-wm1133-ev1-objs := wm1133-ev1.o - -obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o -obj-$(CONFIG_SND_SOC_PHYCORE_AC97) += snd-soc-phycore-ac97.o -obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o -obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o diff --git a/sound/soc/imx/eukrea-tlv320.c b/sound/soc/imx/eukrea-tlv320.c deleted file mode 100644 index 7d4475cfdb24..000000000000 --- a/sound/soc/imx/eukrea-tlv320.c +++ /dev/null @@ -1,164 +0,0 @@ -/* - * eukrea-tlv320.c -- SoC audio for eukrea_cpuimxXX in I2S mode - * - * Copyright 2010 Eric Bénard, Eukréa Electromatique - * - * based on sound/soc/s3c24xx/s3c24xx_simtec_tlv320aic23.c - * which is Copyright 2009 Simtec Electronics - * and on sound/soc/imx/phycore-ac97.c which is - * Copyright 2009 Sascha Hauer, Pengutronix - * - * 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; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../codecs/tlv320aic23.h" -#include "imx-ssi.h" -#include "imx-audmux.h" - -#define CODEC_CLOCK 12000000 - -static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret; - - ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); - if (ret) { - pr_err("%s: failed set cpu dai format\n", __func__); - return ret; - } - - ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S | - SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM); - if (ret) { - pr_err("%s: failed set codec dai format\n", __func__); - return ret; - } - - ret = snd_soc_dai_set_sysclk(codec_dai, 0, - CODEC_CLOCK, SND_SOC_CLOCK_OUT); - if (ret) { - pr_err("%s: failed setting codec sysclk\n", __func__); - return ret; - } - snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0); - - ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, - SND_SOC_CLOCK_IN); - if (ret) { - pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n"); - return ret; - } - - return 0; -} - -static struct snd_soc_ops eukrea_tlv320_snd_ops = { - .hw_params = eukrea_tlv320_hw_params, -}; - -static struct snd_soc_dai_link eukrea_tlv320_dai = { - .name = "tlv320aic23", - .stream_name = "TLV320AIC23", - .codec_dai_name = "tlv320aic23-hifi", - .platform_name = "imx-fiq-pcm-audio.0", - .codec_name = "tlv320aic23-codec.0-001a", - .cpu_dai_name = "imx-ssi.0", - .ops = &eukrea_tlv320_snd_ops, -}; - -static struct snd_soc_card eukrea_tlv320 = { - .name = "cpuimx-audio", - .owner = THIS_MODULE, - .dai_link = &eukrea_tlv320_dai, - .num_links = 1, -}; - -static struct platform_device *eukrea_tlv320_snd_device; - -static int __init eukrea_tlv320_init(void) -{ - int ret; - int int_port = 0, ext_port; - - if (machine_is_eukrea_cpuimx27()) { - imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, - IMX_AUDMUX_V1_PCR_SYN | - IMX_AUDMUX_V1_PCR_TFSDIR | - IMX_AUDMUX_V1_PCR_TCLKDIR | - IMX_AUDMUX_V1_PCR_RFSDIR | - IMX_AUDMUX_V1_PCR_RCLKDIR | - IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) | - IMX_AUDMUX_V1_PCR_RFCSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) | - IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR3_SSI_PINS_4) - ); - imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR3_SSI_PINS_4, - IMX_AUDMUX_V1_PCR_SYN | - IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) - ); - } else if (machine_is_eukrea_cpuimx25sd() || - machine_is_eukrea_cpuimx35sd() || - machine_is_eukrea_cpuimx51sd()) { - ext_port = machine_is_eukrea_cpuimx25sd() ? 4 : 3; - imx_audmux_v2_configure_port(int_port, - IMX_AUDMUX_V2_PTCR_SYN | - IMX_AUDMUX_V2_PTCR_TFSDIR | - IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | - IMX_AUDMUX_V2_PTCR_TCLKDIR | - IMX_AUDMUX_V2_PTCR_TCSEL(ext_port), - IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port) - ); - imx_audmux_v2_configure_port(ext_port, - IMX_AUDMUX_V2_PTCR_SYN, - IMX_AUDMUX_V2_PDCR_RXDSEL(int_port) - ); - } else { - /* return happy. We might run on a totally different machine */ - return 0; - } - - eukrea_tlv320_snd_device = platform_device_alloc("soc-audio", -1); - if (!eukrea_tlv320_snd_device) - return -ENOMEM; - - platform_set_drvdata(eukrea_tlv320_snd_device, &eukrea_tlv320); - ret = platform_device_add(eukrea_tlv320_snd_device); - - if (ret) { - printk(KERN_ERR "ASoC: Platform device allocation failed\n"); - platform_device_put(eukrea_tlv320_snd_device); - } - - return ret; -} - -static void __exit eukrea_tlv320_exit(void) -{ - platform_device_unregister(eukrea_tlv320_snd_device); -} - -module_init(eukrea_tlv320_init); -module_exit(eukrea_tlv320_exit); - -MODULE_AUTHOR("Eric Bénard "); -MODULE_DESCRIPTION("CPUIMX ALSA SoC driver"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/imx/imx-audmux.c b/sound/soc/imx/imx-audmux.c deleted file mode 100644 index 601df809a26a..000000000000 --- a/sound/soc/imx/imx-audmux.c +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright 2012 Freescale Semiconductor, Inc. - * Copyright 2012 Linaro Ltd. - * Copyright 2009 Pengutronix, Sascha Hauer - * - * Initial development of this code was funded by - * Phytec Messtechnik GmbH, http://www.phytec.de - * - * 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "imx-audmux.h" - -#define DRIVER_NAME "imx-audmux" - -static struct clk *audmux_clk; -static void __iomem *audmux_base; - -#define IMX_AUDMUX_V2_PTCR(x) ((x) * 8) -#define IMX_AUDMUX_V2_PDCR(x) ((x) * 8 + 4) - -#ifdef CONFIG_DEBUG_FS -static struct dentry *audmux_debugfs_root; - -static int audmux_open_file(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -/* There is an annoying discontinuity in the SSI numbering with regard - * to the Linux number of the devices */ -static const char *audmux_port_string(int port) -{ - switch (port) { - case MX31_AUDMUX_PORT1_SSI0: - return "imx-ssi.0"; - case MX31_AUDMUX_PORT2_SSI1: - return "imx-ssi.1"; - case MX31_AUDMUX_PORT3_SSI_PINS_3: - return "SSI3"; - case MX31_AUDMUX_PORT4_SSI_PINS_4: - return "SSI4"; - case MX31_AUDMUX_PORT5_SSI_PINS_5: - return "SSI5"; - case MX31_AUDMUX_PORT6_SSI_PINS_6: - return "SSI6"; - default: - return "UNKNOWN"; - } -} - -static ssize_t audmux_read_file(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - ssize_t ret; - char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); - int port = (int)file->private_data; - u32 pdcr, ptcr; - - if (!buf) - return -ENOMEM; - - if (audmux_clk) - clk_prepare_enable(audmux_clk); - - ptcr = readl(audmux_base + IMX_AUDMUX_V2_PTCR(port)); - pdcr = readl(audmux_base + IMX_AUDMUX_V2_PDCR(port)); - - if (audmux_clk) - clk_disable_unprepare(audmux_clk); - - ret = snprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n", - pdcr, ptcr); - - if (ptcr & IMX_AUDMUX_V2_PTCR_TFSDIR) - ret += snprintf(buf + ret, PAGE_SIZE - ret, - "TxFS output from %s, ", - audmux_port_string((ptcr >> 27) & 0x7)); - else - ret += snprintf(buf + ret, PAGE_SIZE - ret, - "TxFS input, "); - - if (ptcr & IMX_AUDMUX_V2_PTCR_TCLKDIR) - ret += snprintf(buf + ret, PAGE_SIZE - ret, - "TxClk output from %s", - audmux_port_string((ptcr >> 22) & 0x7)); - else - ret += snprintf(buf + ret, PAGE_SIZE - ret, - "TxClk input"); - - ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n"); - - if (ptcr & IMX_AUDMUX_V2_PTCR_SYN) { - ret += snprintf(buf + ret, PAGE_SIZE - ret, - "Port is symmetric"); - } else { - if (ptcr & IMX_AUDMUX_V2_PTCR_RFSDIR) - ret += snprintf(buf + ret, PAGE_SIZE - ret, - "RxFS output from %s, ", - audmux_port_string((ptcr >> 17) & 0x7)); - else - ret += snprintf(buf + ret, PAGE_SIZE - ret, - "RxFS input, "); - - if (ptcr & IMX_AUDMUX_V2_PTCR_RCLKDIR) - ret += snprintf(buf + ret, PAGE_SIZE - ret, - "RxClk output from %s", - audmux_port_string((ptcr >> 12) & 0x7)); - else - ret += snprintf(buf + ret, PAGE_SIZE - ret, - "RxClk input"); - } - - ret += snprintf(buf + ret, PAGE_SIZE - ret, - "\nData received from %s\n", - audmux_port_string((pdcr >> 13) & 0x7)); - - ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); - - kfree(buf); - - return ret; -} - -static const struct file_operations audmux_debugfs_fops = { - .open = audmux_open_file, - .read = audmux_read_file, - .llseek = default_llseek, -}; - -static void __init audmux_debugfs_init(void) -{ - int i; - char buf[20]; - - audmux_debugfs_root = debugfs_create_dir("audmux", NULL); - if (!audmux_debugfs_root) { - pr_warning("Failed to create AUDMUX debugfs root\n"); - return; - } - - for (i = 1; i < 8; i++) { - snprintf(buf, sizeof(buf), "ssi%d", i); - if (!debugfs_create_file(buf, 0444, audmux_debugfs_root, - (void *)i, &audmux_debugfs_fops)) - pr_warning("Failed to create AUDMUX port %d debugfs file\n", - i); - } -} - -static void __devexit audmux_debugfs_remove(void) -{ - debugfs_remove_recursive(audmux_debugfs_root); -} -#else -static inline void audmux_debugfs_init(void) -{ -} - -static inline void audmux_debugfs_remove(void) -{ -} -#endif - -enum imx_audmux_type { - IMX21_AUDMUX, - IMX31_AUDMUX, -} audmux_type; - -static struct platform_device_id imx_audmux_ids[] = { - { - .name = "imx21-audmux", - .driver_data = IMX21_AUDMUX, - }, { - .name = "imx31-audmux", - .driver_data = IMX31_AUDMUX, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, imx_audmux_ids); - -static const struct of_device_id imx_audmux_dt_ids[] = { - { .compatible = "fsl,imx21-audmux", .data = &imx_audmux_ids[0], }, - { .compatible = "fsl,imx31-audmux", .data = &imx_audmux_ids[1], }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, imx_audmux_dt_ids); - -static const uint8_t port_mapping[] = { - 0x0, 0x4, 0x8, 0x10, 0x14, 0x1c, -}; - -int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr) -{ - if (audmux_type != IMX21_AUDMUX) - return -EINVAL; - - if (!audmux_base) - return -ENOSYS; - - if (port >= ARRAY_SIZE(port_mapping)) - return -EINVAL; - - writel(pcr, audmux_base + port_mapping[port]); - - return 0; -} -EXPORT_SYMBOL_GPL(imx_audmux_v1_configure_port); - -int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, - unsigned int pdcr) -{ - if (audmux_type != IMX31_AUDMUX) - return -EINVAL; - - if (!audmux_base) - return -ENOSYS; - - if (audmux_clk) - clk_prepare_enable(audmux_clk); - - writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port)); - writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port)); - - if (audmux_clk) - clk_disable_unprepare(audmux_clk); - - return 0; -} -EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port); - -static int __devinit imx_audmux_probe(struct platform_device *pdev) -{ - struct resource *res; - const struct of_device_id *of_id = - of_match_device(imx_audmux_dt_ids, &pdev->dev); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - audmux_base = devm_request_and_ioremap(&pdev->dev, res); - if (!audmux_base) - return -EADDRNOTAVAIL; - - audmux_clk = clk_get(&pdev->dev, "audmux"); - if (IS_ERR(audmux_clk)) { - dev_dbg(&pdev->dev, "cannot get clock: %ld\n", - PTR_ERR(audmux_clk)); - audmux_clk = NULL; - } - - if (of_id) - pdev->id_entry = of_id->data; - audmux_type = pdev->id_entry->driver_data; - if (audmux_type == IMX31_AUDMUX) - audmux_debugfs_init(); - - return 0; -} - -static int __devexit imx_audmux_remove(struct platform_device *pdev) -{ - if (audmux_type == IMX31_AUDMUX) - audmux_debugfs_remove(); - clk_put(audmux_clk); - - return 0; -} - -static struct platform_driver imx_audmux_driver = { - .probe = imx_audmux_probe, - .remove = __devexit_p(imx_audmux_remove), - .id_table = imx_audmux_ids, - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - .of_match_table = imx_audmux_dt_ids, - } -}; - -static int __init imx_audmux_init(void) -{ - return platform_driver_register(&imx_audmux_driver); -} -subsys_initcall(imx_audmux_init); - -static void __exit imx_audmux_exit(void) -{ - platform_driver_unregister(&imx_audmux_driver); -} -module_exit(imx_audmux_exit); - -MODULE_DESCRIPTION("Freescale i.MX AUDMUX driver"); -MODULE_AUTHOR("Sascha Hauer "); -MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/sound/soc/imx/imx-audmux.h b/sound/soc/imx/imx-audmux.h deleted file mode 100644 index 04ebbab8d7b9..000000000000 --- a/sound/soc/imx/imx-audmux.h +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef __IMX_AUDMUX_H -#define __IMX_AUDMUX_H - -#define MX27_AUDMUX_HPCR1_SSI0 0 -#define MX27_AUDMUX_HPCR2_SSI1 1 -#define MX27_AUDMUX_HPCR3_SSI_PINS_4 2 -#define MX27_AUDMUX_PPCR1_SSI_PINS_1 3 -#define MX27_AUDMUX_PPCR2_SSI_PINS_2 4 -#define MX27_AUDMUX_PPCR3_SSI_PINS_3 5 - -#define MX31_AUDMUX_PORT1_SSI0 0 -#define MX31_AUDMUX_PORT2_SSI1 1 -#define MX31_AUDMUX_PORT3_SSI_PINS_3 2 -#define MX31_AUDMUX_PORT4_SSI_PINS_4 3 -#define MX31_AUDMUX_PORT5_SSI_PINS_5 4 -#define MX31_AUDMUX_PORT6_SSI_PINS_6 5 - -#define MX51_AUDMUX_PORT1_SSI0 0 -#define MX51_AUDMUX_PORT2_SSI1 1 -#define MX51_AUDMUX_PORT3 2 -#define MX51_AUDMUX_PORT4 3 -#define MX51_AUDMUX_PORT5 4 -#define MX51_AUDMUX_PORT6 5 -#define MX51_AUDMUX_PORT7 6 - -/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */ -#define IMX_AUDMUX_V1_PCR_INMMASK(x) ((x) & 0xff) -#define IMX_AUDMUX_V1_PCR_INMEN (1 << 8) -#define IMX_AUDMUX_V1_PCR_TXRXEN (1 << 10) -#define IMX_AUDMUX_V1_PCR_SYN (1 << 12) -#define IMX_AUDMUX_V1_PCR_RXDSEL(x) (((x) & 0x7) << 13) -#define IMX_AUDMUX_V1_PCR_RFCSEL(x) (((x) & 0xf) << 20) -#define IMX_AUDMUX_V1_PCR_RCLKDIR (1 << 24) -#define IMX_AUDMUX_V1_PCR_RFSDIR (1 << 25) -#define IMX_AUDMUX_V1_PCR_TFCSEL(x) (((x) & 0xf) << 26) -#define IMX_AUDMUX_V1_PCR_TCLKDIR (1 << 30) -#define IMX_AUDMUX_V1_PCR_TFSDIR (1 << 31) - -/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */ -#define IMX_AUDMUX_V2_PTCR_TFSDIR (1 << 31) -#define IMX_AUDMUX_V2_PTCR_TFSEL(x) (((x) & 0xf) << 27) -#define IMX_AUDMUX_V2_PTCR_TCLKDIR (1 << 26) -#define IMX_AUDMUX_V2_PTCR_TCSEL(x) (((x) & 0xf) << 22) -#define IMX_AUDMUX_V2_PTCR_RFSDIR (1 << 21) -#define IMX_AUDMUX_V2_PTCR_RFSEL(x) (((x) & 0xf) << 17) -#define IMX_AUDMUX_V2_PTCR_RCLKDIR (1 << 16) -#define IMX_AUDMUX_V2_PTCR_RCSEL(x) (((x) & 0xf) << 12) -#define IMX_AUDMUX_V2_PTCR_SYN (1 << 11) - -#define IMX_AUDMUX_V2_PDCR_RXDSEL(x) (((x) & 0x7) << 13) -#define IMX_AUDMUX_V2_PDCR_TXRXEN (1 << 12) -#define IMX_AUDMUX_V2_PDCR_MODE(x) (((x) & 0x3) << 8) -#define IMX_AUDMUX_V2_PDCR_INMMASK(x) ((x) & 0xff) - -int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr); - -int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr, - unsigned int pdcr); - -#endif /* __IMX_AUDMUX_H */ diff --git a/sound/soc/imx/imx-pcm-dma-mx2.c b/sound/soc/imx/imx-pcm-dma-mx2.c deleted file mode 100644 index 6b818de2fc03..000000000000 --- a/sound/soc/imx/imx-pcm-dma-mx2.c +++ /dev/null @@ -1,175 +0,0 @@ -/* - * imx-pcm-dma-mx2.c -- ALSA Soc Audio Layer - * - * Copyright 2009 Sascha Hauer - * - * This code is based on code copyrighted by Freescale, - * Liam Girdwood, Javier Martin and probably others. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -#include "imx-pcm.h" - -static bool filter(struct dma_chan *chan, void *param) -{ - if (!imx_dma_is_general_purpose(chan)) - return false; - - chan->private = param; - - return true; -} - -static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); - struct imx_pcm_dma_params *dma_params; - struct dma_slave_config slave_config; - int ret; - - dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config); - if (ret) - return ret; - - slave_config.device_fc = false; - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - slave_config.dst_addr = dma_params->dma_addr; - slave_config.dst_maxburst = dma_params->burstsize; - } else { - slave_config.src_addr = dma_params->dma_addr; - slave_config.src_maxburst = dma_params->burstsize; - } - - ret = dmaengine_slave_config(chan, &slave_config); - if (ret) - return ret; - - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - - return 0; -} - -static struct snd_pcm_hardware snd_imx_hardware = { - .info = SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_RESUME, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rate_min = 8000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, - .period_bytes_min = 128, - .period_bytes_max = 65535, /* Limited by SDMA engine */ - .periods_min = 2, - .periods_max = 255, - .fifo_size = 0, -}; - -static int snd_imx_open(struct snd_pcm_substream *substream) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct imx_pcm_dma_params *dma_params; - struct imx_dma_data *dma_data; - int ret; - - snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); - - dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); - - dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL); - dma_data->peripheral_type = IMX_DMATYPE_SSI; - dma_data->priority = DMA_PRIO_HIGH; - dma_data->dma_request = dma_params->dma; - - ret = snd_dmaengine_pcm_open(substream, filter, dma_data); - if (ret) { - kfree(dma_data); - return 0; - } - - snd_dmaengine_pcm_set_data(substream, dma_data); - - return 0; -} - -static int snd_imx_close(struct snd_pcm_substream *substream) -{ - struct imx_dma_data *dma_data = snd_dmaengine_pcm_get_data(substream); - - snd_dmaengine_pcm_close(substream); - kfree(dma_data); - - return 0; -} - -static struct snd_pcm_ops imx_pcm_ops = { - .open = snd_imx_open, - .close = snd_imx_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_imx_pcm_hw_params, - .trigger = snd_dmaengine_pcm_trigger, - .pointer = snd_dmaengine_pcm_pointer, - .mmap = snd_imx_pcm_mmap, -}; - -static struct snd_soc_platform_driver imx_soc_platform_mx2 = { - .ops = &imx_pcm_ops, - .pcm_new = imx_pcm_new, - .pcm_free = imx_pcm_free, -}; - -static int __devinit imx_soc_platform_probe(struct platform_device *pdev) -{ - return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2); -} - -static int __devexit imx_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -static struct platform_driver imx_pcm_driver = { - .driver = { - .name = "imx-pcm-audio", - .owner = THIS_MODULE, - }, - .probe = imx_soc_platform_probe, - .remove = __devexit_p(imx_soc_platform_remove), -}; - -module_platform_driver(imx_pcm_driver); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:imx-pcm-audio"); diff --git a/sound/soc/imx/imx-pcm-fiq.c b/sound/soc/imx/imx-pcm-fiq.c deleted file mode 100644 index 456b7d723d66..000000000000 --- a/sound/soc/imx/imx-pcm-fiq.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * imx-pcm-fiq.c -- ALSA Soc Audio Layer - * - * Copyright 2009 Sascha Hauer - * - * This code is based on code copyrighted by Freescale, - * Liam Girdwood, Javier Martin and probably others. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include - -#include - -#include "imx-ssi.h" - -struct imx_pcm_runtime_data { - int period; - int periods; - unsigned long offset; - unsigned long last_offset; - unsigned long size; - struct hrtimer hrt; - int poll_time_ns; - struct snd_pcm_substream *substream; - atomic_t running; -}; - -static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) -{ - struct imx_pcm_runtime_data *iprtd = - container_of(hrt, struct imx_pcm_runtime_data, hrt); - struct snd_pcm_substream *substream = iprtd->substream; - struct snd_pcm_runtime *runtime = substream->runtime; - struct pt_regs regs; - unsigned long delta; - - if (!atomic_read(&iprtd->running)) - return HRTIMER_NORESTART; - - get_fiq_regs(®s); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - iprtd->offset = regs.ARM_r8 & 0xffff; - else - iprtd->offset = regs.ARM_r9 & 0xffff; - - /* How much data have we transferred since the last period report? */ - if (iprtd->offset >= iprtd->last_offset) - delta = iprtd->offset - iprtd->last_offset; - else - delta = runtime->buffer_size + iprtd->offset - - iprtd->last_offset; - - /* If we've transferred at least a period then report it and - * reset our poll time */ - if (delta >= iprtd->period) { - snd_pcm_period_elapsed(substream); - iprtd->last_offset = iprtd->offset; - } - - hrtimer_forward_now(hrt, ns_to_ktime(iprtd->poll_time_ns)); - - return HRTIMER_RESTART; -} - -static struct fiq_handler fh = { - .name = DRV_NAME, -}; - -static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct imx_pcm_runtime_data *iprtd = runtime->private_data; - - iprtd->size = params_buffer_bytes(params); - iprtd->periods = params_periods(params); - iprtd->period = params_period_bytes(params) ; - iprtd->offset = 0; - iprtd->last_offset = 0; - iprtd->poll_time_ns = 1000000000 / params_rate(params) * - params_period_size(params); - snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); - - return 0; -} - -static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct imx_pcm_runtime_data *iprtd = runtime->private_data; - struct pt_regs regs; - - get_fiq_regs(®s); - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - regs.ARM_r8 = (iprtd->period * iprtd->periods - 1) << 16; - else - regs.ARM_r9 = (iprtd->period * iprtd->periods - 1) << 16; - - set_fiq_regs(®s); - - return 0; -} - -static int fiq_enable; -static int imx_pcm_fiq; - -static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct imx_pcm_runtime_data *iprtd = runtime->private_data; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - atomic_set(&iprtd->running, 1); - hrtimer_start(&iprtd->hrt, ns_to_ktime(iprtd->poll_time_ns), - HRTIMER_MODE_REL); - if (++fiq_enable == 1) - enable_fiq(imx_pcm_fiq); - - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - atomic_set(&iprtd->running, 0); - - if (--fiq_enable == 0) - disable_fiq(imx_pcm_fiq); - - break; - default: - return -EINVAL; - } - - return 0; -} - -static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct imx_pcm_runtime_data *iprtd = runtime->private_data; - - return bytes_to_frames(substream->runtime, iprtd->offset); -} - -static struct snd_pcm_hardware snd_imx_hardware = { - .info = SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_MMAP_VALID | - SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_RESUME, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rate_min = 8000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, - .period_bytes_min = 128, - .period_bytes_max = 16 * 1024, - .periods_min = 4, - .periods_max = 255, - .fifo_size = 0, -}; - -static int snd_imx_open(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct imx_pcm_runtime_data *iprtd; - int ret; - - iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL); - if (iprtd == NULL) - return -ENOMEM; - runtime->private_data = iprtd; - - iprtd->substream = substream; - - atomic_set(&iprtd->running, 0); - hrtimer_init(&iprtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - iprtd->hrt.function = snd_hrtimer_callback; - - ret = snd_pcm_hw_constraint_integer(substream->runtime, - SNDRV_PCM_HW_PARAM_PERIODS); - if (ret < 0) { - kfree(iprtd); - return ret; - } - - snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); - return 0; -} - -static int snd_imx_close(struct snd_pcm_substream *substream) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct imx_pcm_runtime_data *iprtd = runtime->private_data; - - hrtimer_cancel(&iprtd->hrt); - - kfree(iprtd); - - return 0; -} - -static struct snd_pcm_ops imx_pcm_ops = { - .open = snd_imx_open, - .close = snd_imx_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_imx_pcm_hw_params, - .prepare = snd_imx_pcm_prepare, - .trigger = snd_imx_pcm_trigger, - .pointer = snd_imx_pcm_pointer, - .mmap = snd_imx_pcm_mmap, -}; - -static int ssi_irq = 0; - -static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_pcm *pcm = rtd->pcm; - struct snd_pcm_substream *substream; - int ret; - - ret = imx_pcm_new(rtd); - if (ret) - return ret; - - substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; - if (substream) { - struct snd_dma_buffer *buf = &substream->dma_buffer; - - imx_ssi_fiq_tx_buffer = (unsigned long)buf->area; - } - - substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - if (substream) { - struct snd_dma_buffer *buf = &substream->dma_buffer; - - imx_ssi_fiq_rx_buffer = (unsigned long)buf->area; - } - - set_fiq_handler(&imx_ssi_fiq_start, - &imx_ssi_fiq_end - &imx_ssi_fiq_start); - - return 0; -} - -static void imx_pcm_fiq_free(struct snd_pcm *pcm) -{ - mxc_set_irq_fiq(ssi_irq, 0); - release_fiq(&fh); - imx_pcm_free(pcm); -} - -static struct snd_soc_platform_driver imx_soc_platform_fiq = { - .ops = &imx_pcm_ops, - .pcm_new = imx_pcm_fiq_new, - .pcm_free = imx_pcm_fiq_free, -}; - -static int __devinit imx_soc_platform_probe(struct platform_device *pdev) -{ - struct imx_ssi *ssi = platform_get_drvdata(pdev); - int ret; - - ret = claim_fiq(&fh); - if (ret) { - dev_err(&pdev->dev, "failed to claim fiq: %d", ret); - return ret; - } - - mxc_set_irq_fiq(ssi->irq, 1); - ssi_irq = ssi->irq; - - imx_pcm_fiq = ssi->irq; - - imx_ssi_fiq_base = (unsigned long)ssi->base; - - ssi->dma_params_tx.burstsize = 4; - ssi->dma_params_rx.burstsize = 6; - - ret = snd_soc_register_platform(&pdev->dev, &imx_soc_platform_fiq); - if (ret) - goto failed_register; - - return 0; - -failed_register: - mxc_set_irq_fiq(ssi_irq, 0); - release_fiq(&fh); - - return ret; -} - -static int __devexit imx_soc_platform_remove(struct platform_device *pdev) -{ - snd_soc_unregister_platform(&pdev->dev); - return 0; -} - -static struct platform_driver imx_pcm_driver = { - .driver = { - .name = "imx-fiq-pcm-audio", - .owner = THIS_MODULE, - }, - - .probe = imx_soc_platform_probe, - .remove = __devexit_p(imx_soc_platform_remove), -}; - -module_platform_driver(imx_pcm_driver); - -MODULE_LICENSE("GPL"); diff --git a/sound/soc/imx/imx-pcm.c b/sound/soc/imx/imx-pcm.c deleted file mode 100644 index 93dc360b1777..000000000000 --- a/sound/soc/imx/imx-pcm.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2009 Sascha Hauer - * - * This code is based on code copyrighted by Freescale, - * Liam Girdwood, Javier Martin and probably others. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include "imx-pcm.h" - -int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - int ret; - - ret = dma_mmap_writecombine(substream->pcm->card->dev, vma, - runtime->dma_area, runtime->dma_addr, runtime->dma_bytes); - - pr_debug("%s: ret: %d %p 0x%08x 0x%08x\n", __func__, ret, - runtime->dma_area, - runtime->dma_addr, - runtime->dma_bytes); - return ret; -} -EXPORT_SYMBOL_GPL(snd_imx_pcm_mmap); - -static int imx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) -{ - struct snd_pcm_substream *substream = pcm->streams[stream].substream; - struct snd_dma_buffer *buf = &substream->dma_buffer; - size_t size = IMX_SSI_DMABUF_SIZE; - - buf->dev.type = SNDRV_DMA_TYPE_DEV; - buf->dev.dev = pcm->card->dev; - buf->private_data = NULL; - buf->area = dma_alloc_writecombine(pcm->card->dev, size, - &buf->addr, GFP_KERNEL); - if (!buf->area) - return -ENOMEM; - buf->bytes = size; - - return 0; -} - -static u64 imx_pcm_dmamask = DMA_BIT_MASK(32); - -int imx_pcm_new(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_card *card = rtd->card->snd_card; - struct snd_pcm *pcm = rtd->pcm; - int ret = 0; - - if (!card->dev->dma_mask) - card->dev->dma_mask = &imx_pcm_dmamask; - if (!card->dev->coherent_dma_mask) - card->dev->coherent_dma_mask = DMA_BIT_MASK(32); - if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { - ret = imx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_PLAYBACK); - if (ret) - goto out; - } - - if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { - ret = imx_pcm_preallocate_dma_buffer(pcm, - SNDRV_PCM_STREAM_CAPTURE); - if (ret) - goto out; - } - -out: - return ret; -} -EXPORT_SYMBOL_GPL(imx_pcm_new); - -void imx_pcm_free(struct snd_pcm *pcm) -{ - struct snd_pcm_substream *substream; - struct snd_dma_buffer *buf; - int stream; - - for (stream = 0; stream < 2; stream++) { - substream = pcm->streams[stream].substream; - if (!substream) - continue; - - buf = &substream->dma_buffer; - if (!buf->area) - continue; - - dma_free_writecombine(pcm->card->dev, buf->bytes, - buf->area, buf->addr); - buf->area = NULL; - } -} -EXPORT_SYMBOL_GPL(imx_pcm_free); diff --git a/sound/soc/imx/imx-pcm.h b/sound/soc/imx/imx-pcm.h deleted file mode 100644 index b5f5c3acf34d..000000000000 --- a/sound/soc/imx/imx-pcm.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2009 Sascha Hauer - * - * This code is based on code copyrighted by Freescale, - * Liam Girdwood, Javier Martin and probably others. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef _IMX_PCM_H -#define _IMX_PCM_H - -/* - * Do not change this as the FIQ handler depends on this size - */ -#define IMX_SSI_DMABUF_SIZE (64 * 1024) - -struct imx_pcm_dma_params { - int dma; - unsigned long dma_addr; - int burstsize; -}; - -int snd_imx_pcm_mmap(struct snd_pcm_substream *substream, - struct vm_area_struct *vma); -int imx_pcm_new(struct snd_soc_pcm_runtime *rtd); -void imx_pcm_free(struct snd_pcm *pcm); - -#endif /* _IMX_PCM_H */ diff --git a/sound/soc/imx/imx-ssi.c b/sound/soc/imx/imx-ssi.c deleted file mode 100644 index 4f81ed456325..000000000000 --- a/sound/soc/imx/imx-ssi.c +++ /dev/null @@ -1,690 +0,0 @@ -/* - * imx-ssi.c -- ALSA Soc Audio Layer - * - * Copyright 2009 Sascha Hauer - * - * This code is based on code copyrighted by Freescale, - * Liam Girdwood, Javier Martin and probably others. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - * - * - * The i.MX SSI core has some nasty limitations in AC97 mode. While most - * sane processor vendors have a FIFO per AC97 slot, the i.MX has only - * one FIFO which combines all valid receive slots. We cannot even select - * which slots we want to receive. The WM9712 with which this driver - * was developed with always sends GPIO status data in slot 12 which - * we receive in our (PCM-) data stream. The only chance we have is to - * manually skip this data in the FIQ handler. With sampling rates different - * from 48000Hz not every frame has valid receive data, so the ratio - * between pcm data and GPIO status data changes. Our FIQ handler is not - * able to handle this, hence this driver only works with 48000Hz sampling - * rate. - * Reading and writing AC97 registers is another challenge. The core - * provides us status bits when the read register is updated with *another* - * value. When we read the same register two times (and the register still - * contains the same value) these status bits are not set. We work - * around this by not polling these bits but only wait a fixed delay. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -#include "imx-ssi.h" - -#define SSI_SACNT_DEFAULT (SSI_SACNT_AC97EN | SSI_SACNT_FV) - -/* - * SSI Network Mode or TDM slots configuration. - * Should only be called when port is inactive (i.e. SSIEN = 0). - */ -static int imx_ssi_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, - unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) -{ - struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); - u32 sccr; - - sccr = readl(ssi->base + SSI_STCCR); - sccr &= ~SSI_STCCR_DC_MASK; - sccr |= SSI_STCCR_DC(slots - 1); - writel(sccr, ssi->base + SSI_STCCR); - - sccr = readl(ssi->base + SSI_SRCCR); - sccr &= ~SSI_STCCR_DC_MASK; - sccr |= SSI_STCCR_DC(slots - 1); - writel(sccr, ssi->base + SSI_SRCCR); - - writel(tx_mask, ssi->base + SSI_STMSK); - writel(rx_mask, ssi->base + SSI_SRMSK); - - return 0; -} - -/* - * SSI DAI format configuration. - * Should only be called when port is inactive (i.e. SSIEN = 0). - */ -static int imx_ssi_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt) -{ - struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); - u32 strcr = 0, scr; - - scr = readl(ssi->base + SSI_SCR) & ~(SSI_SCR_SYN | SSI_SCR_NET); - - /* DAI mode */ - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { - case SND_SOC_DAIFMT_I2S: - /* data on rising edge of bclk, frame low 1clk before data */ - strcr |= SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TXBIT0; - scr |= SSI_SCR_NET; - if (ssi->flags & IMX_SSI_USE_I2S_SLAVE) { - scr &= ~SSI_I2S_MODE_MASK; - scr |= SSI_SCR_I2S_MODE_SLAVE; - } - break; - case SND_SOC_DAIFMT_LEFT_J: - /* data on rising edge of bclk, frame high with data */ - strcr |= SSI_STCR_TXBIT0; - break; - case SND_SOC_DAIFMT_DSP_B: - /* data on rising edge of bclk, frame high with data */ - strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0; - break; - case SND_SOC_DAIFMT_DSP_A: - /* data on rising edge of bclk, frame high 1clk before data */ - strcr |= SSI_STCR_TFSL | SSI_STCR_TXBIT0 | SSI_STCR_TEFS; - break; - } - - /* DAI clock inversion */ - switch (fmt & SND_SOC_DAIFMT_INV_MASK) { - case SND_SOC_DAIFMT_IB_IF: - strcr |= SSI_STCR_TFSI; - strcr &= ~SSI_STCR_TSCKP; - break; - case SND_SOC_DAIFMT_IB_NF: - strcr &= ~(SSI_STCR_TSCKP | SSI_STCR_TFSI); - break; - case SND_SOC_DAIFMT_NB_IF: - strcr |= SSI_STCR_TFSI | SSI_STCR_TSCKP; - break; - case SND_SOC_DAIFMT_NB_NF: - strcr &= ~SSI_STCR_TFSI; - strcr |= SSI_STCR_TSCKP; - break; - } - - /* DAI clock master masks */ - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { - case SND_SOC_DAIFMT_CBM_CFM: - break; - default: - /* Master mode not implemented, needs handling of clocks. */ - return -EINVAL; - } - - strcr |= SSI_STCR_TFEN0; - - if (ssi->flags & IMX_SSI_NET) - scr |= SSI_SCR_NET; - if (ssi->flags & IMX_SSI_SYN) - scr |= SSI_SCR_SYN; - - writel(strcr, ssi->base + SSI_STCR); - writel(strcr, ssi->base + SSI_SRCR); - writel(scr, ssi->base + SSI_SCR); - - return 0; -} - -/* - * SSI system clock configuration. - * Should only be called when port is inactive (i.e. SSIEN = 0). - */ -static int imx_ssi_set_dai_sysclk(struct snd_soc_dai *cpu_dai, - int clk_id, unsigned int freq, int dir) -{ - struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); - u32 scr; - - scr = readl(ssi->base + SSI_SCR); - - switch (clk_id) { - case IMX_SSP_SYS_CLK: - if (dir == SND_SOC_CLOCK_OUT) - scr |= SSI_SCR_SYS_CLK_EN; - else - scr &= ~SSI_SCR_SYS_CLK_EN; - break; - default: - return -EINVAL; - } - - writel(scr, ssi->base + SSI_SCR); - - return 0; -} - -/* - * SSI Clock dividers - * Should only be called when port is inactive (i.e. SSIEN = 0). - */ -static int imx_ssi_set_dai_clkdiv(struct snd_soc_dai *cpu_dai, - int div_id, int div) -{ - struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); - u32 stccr, srccr; - - stccr = readl(ssi->base + SSI_STCCR); - srccr = readl(ssi->base + SSI_SRCCR); - - switch (div_id) { - case IMX_SSI_TX_DIV_2: - stccr &= ~SSI_STCCR_DIV2; - stccr |= div; - break; - case IMX_SSI_TX_DIV_PSR: - stccr &= ~SSI_STCCR_PSR; - stccr |= div; - break; - case IMX_SSI_TX_DIV_PM: - stccr &= ~0xff; - stccr |= SSI_STCCR_PM(div); - break; - case IMX_SSI_RX_DIV_2: - stccr &= ~SSI_STCCR_DIV2; - stccr |= div; - break; - case IMX_SSI_RX_DIV_PSR: - stccr &= ~SSI_STCCR_PSR; - stccr |= div; - break; - case IMX_SSI_RX_DIV_PM: - stccr &= ~0xff; - stccr |= SSI_STCCR_PM(div); - break; - default: - return -EINVAL; - } - - writel(stccr, ssi->base + SSI_STCCR); - writel(srccr, ssi->base + SSI_SRCCR); - - return 0; -} - -static int imx_ssi_startup(struct snd_pcm_substream *substream, - struct snd_soc_dai *cpu_dai) -{ - struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); - struct imx_pcm_dma_params *dma_data; - - /* Tx/Rx config */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - dma_data = &ssi->dma_params_tx; - else - dma_data = &ssi->dma_params_rx; - - snd_soc_dai_set_dma_data(cpu_dai, substream, dma_data); - - return 0; -} - -/* - * Should only be called when port is inactive (i.e. SSIEN = 0), - * although can be called multiple times by upper layers. - */ -static int imx_ssi_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *cpu_dai) -{ - struct imx_ssi *ssi = snd_soc_dai_get_drvdata(cpu_dai); - u32 reg, sccr; - - /* Tx/Rx config */ - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - reg = SSI_STCCR; - else - reg = SSI_SRCCR; - - if (ssi->flags & IMX_SSI_SYN) - reg = SSI_STCCR; - - sccr = readl(ssi->base + reg) & ~SSI_STCCR_WL_MASK; - - /* DAI data (word) size */ - switch (params_format(params)) { - case SNDRV_PCM_FORMAT_S16_LE: - sccr |= SSI_SRCCR_WL(16); - break; - case SNDRV_PCM_FORMAT_S20_3LE: - sccr |= SSI_SRCCR_WL(20); - break; - case SNDRV_PCM_FORMAT_S24_LE: - sccr |= SSI_SRCCR_WL(24); - break; - } - - writel(sccr, ssi->base + reg); - - return 0; -} - -static int imx_ssi_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) -{ - struct imx_ssi *ssi = snd_soc_dai_get_drvdata(dai); - unsigned int sier_bits, sier; - unsigned int scr; - - scr = readl(ssi->base + SSI_SCR); - sier = readl(ssi->base + SSI_SIER); - - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { - if (ssi->flags & IMX_SSI_DMA) - sier_bits = SSI_SIER_TDMAE; - else - sier_bits = SSI_SIER_TIE | SSI_SIER_TFE0_EN; - } else { - if (ssi->flags & IMX_SSI_DMA) - sier_bits = SSI_SIER_RDMAE; - else - sier_bits = SSI_SIER_RIE | SSI_SIER_RFF0_EN; - } - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - scr |= SSI_SCR_TE; - else - scr |= SSI_SCR_RE; - sier |= sier_bits; - - if (++ssi->enabled == 1) - scr |= SSI_SCR_SSIEN; - - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - scr &= ~SSI_SCR_TE; - else - scr &= ~SSI_SCR_RE; - sier &= ~sier_bits; - - if (--ssi->enabled == 0) - scr &= ~SSI_SCR_SSIEN; - - break; - default: - return -EINVAL; - } - - if (!(ssi->flags & IMX_SSI_USE_AC97)) - /* rx/tx are always enabled to access ac97 registers */ - writel(scr, ssi->base + SSI_SCR); - - writel(sier, ssi->base + SSI_SIER); - - return 0; -} - -static const struct snd_soc_dai_ops imx_ssi_pcm_dai_ops = { - .startup = imx_ssi_startup, - .hw_params = imx_ssi_hw_params, - .set_fmt = imx_ssi_set_dai_fmt, - .set_clkdiv = imx_ssi_set_dai_clkdiv, - .set_sysclk = imx_ssi_set_dai_sysclk, - .set_tdm_slot = imx_ssi_set_dai_tdm_slot, - .trigger = imx_ssi_trigger, -}; - -static int imx_ssi_dai_probe(struct snd_soc_dai *dai) -{ - struct imx_ssi *ssi = dev_get_drvdata(dai->dev); - uint32_t val; - - snd_soc_dai_set_drvdata(dai, ssi); - - val = SSI_SFCSR_TFWM0(ssi->dma_params_tx.burstsize) | - SSI_SFCSR_RFWM0(ssi->dma_params_rx.burstsize); - writel(val, ssi->base + SSI_SFCSR); - - return 0; -} - -static struct snd_soc_dai_driver imx_ssi_dai = { - .probe = imx_ssi_dai_probe, - .playback = { - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .channels_min = 1, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_8000_96000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &imx_ssi_pcm_dai_ops, -}; - -static struct snd_soc_dai_driver imx_ac97_dai = { - .probe = imx_ssi_dai_probe, - .ac97_control = 1, - .playback = { - .stream_name = "AC97 Playback", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .capture = { - .stream_name = "AC97 Capture", - .channels_min = 2, - .channels_max = 2, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, - }, - .ops = &imx_ssi_pcm_dai_ops, -}; - -static void setup_channel_to_ac97(struct imx_ssi *imx_ssi) -{ - void __iomem *base = imx_ssi->base; - - writel(0x0, base + SSI_SCR); - writel(0x0, base + SSI_STCR); - writel(0x0, base + SSI_SRCR); - - writel(SSI_SCR_SYN | SSI_SCR_NET, base + SSI_SCR); - - writel(SSI_SFCSR_RFWM0(8) | - SSI_SFCSR_TFWM0(8) | - SSI_SFCSR_RFWM1(8) | - SSI_SFCSR_TFWM1(8), base + SSI_SFCSR); - - writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_STCCR); - writel(SSI_STCCR_WL(16) | SSI_STCCR_DC(12), base + SSI_SRCCR); - - writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN, base + SSI_SCR); - writel(SSI_SOR_WAIT(3), base + SSI_SOR); - - writel(SSI_SCR_SYN | SSI_SCR_NET | SSI_SCR_SSIEN | - SSI_SCR_TE | SSI_SCR_RE, - base + SSI_SCR); - - writel(SSI_SACNT_DEFAULT, base + SSI_SACNT); - writel(0xff, base + SSI_SACCDIS); - writel(0x300, base + SSI_SACCEN); -} - -static struct imx_ssi *ac97_ssi; - -static void imx_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg, - unsigned short val) -{ - struct imx_ssi *imx_ssi = ac97_ssi; - void __iomem *base = imx_ssi->base; - unsigned int lreg; - unsigned int lval; - - if (reg > 0x7f) - return; - - pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); - - lreg = reg << 12; - writel(lreg, base + SSI_SACADD); - - lval = val << 4; - writel(lval , base + SSI_SACDAT); - - writel(SSI_SACNT_DEFAULT | SSI_SACNT_WR, base + SSI_SACNT); - udelay(100); -} - -static unsigned short imx_ssi_ac97_read(struct snd_ac97 *ac97, - unsigned short reg) -{ - struct imx_ssi *imx_ssi = ac97_ssi; - void __iomem *base = imx_ssi->base; - - unsigned short val = -1; - unsigned int lreg; - - lreg = (reg & 0x7f) << 12 ; - writel(lreg, base + SSI_SACADD); - writel(SSI_SACNT_DEFAULT | SSI_SACNT_RD, base + SSI_SACNT); - - udelay(100); - - val = (readl(base + SSI_SACDAT) >> 4) & 0xffff; - - pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); - - return val; -} - -static void imx_ssi_ac97_reset(struct snd_ac97 *ac97) -{ - struct imx_ssi *imx_ssi = ac97_ssi; - - if (imx_ssi->ac97_reset) - imx_ssi->ac97_reset(ac97); -} - -static void imx_ssi_ac97_warm_reset(struct snd_ac97 *ac97) -{ - struct imx_ssi *imx_ssi = ac97_ssi; - - if (imx_ssi->ac97_warm_reset) - imx_ssi->ac97_warm_reset(ac97); -} - -struct snd_ac97_bus_ops soc_ac97_ops = { - .read = imx_ssi_ac97_read, - .write = imx_ssi_ac97_write, - .reset = imx_ssi_ac97_reset, - .warm_reset = imx_ssi_ac97_warm_reset -}; -EXPORT_SYMBOL_GPL(soc_ac97_ops); - -static int imx_ssi_probe(struct platform_device *pdev) -{ - struct resource *res; - struct imx_ssi *ssi; - struct imx_ssi_platform_data *pdata = pdev->dev.platform_data; - int ret = 0; - struct snd_soc_dai_driver *dai; - - ssi = kzalloc(sizeof(*ssi), GFP_KERNEL); - if (!ssi) - return -ENOMEM; - dev_set_drvdata(&pdev->dev, ssi); - - if (pdata) { - ssi->ac97_reset = pdata->ac97_reset; - ssi->ac97_warm_reset = pdata->ac97_warm_reset; - ssi->flags = pdata->flags; - } - - ssi->irq = platform_get_irq(pdev, 0); - - ssi->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(ssi->clk)) { - ret = PTR_ERR(ssi->clk); - dev_err(&pdev->dev, "Cannot get the clock: %d\n", - ret); - goto failed_clk; - } - clk_enable(ssi->clk); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENODEV; - goto failed_get_resource; - } - - if (!request_mem_region(res->start, resource_size(res), DRV_NAME)) { - dev_err(&pdev->dev, "request_mem_region failed\n"); - ret = -EBUSY; - goto failed_get_resource; - } - - ssi->base = ioremap(res->start, resource_size(res)); - if (!ssi->base) { - dev_err(&pdev->dev, "ioremap failed\n"); - ret = -ENODEV; - goto failed_ioremap; - } - - if (ssi->flags & IMX_SSI_USE_AC97) { - if (ac97_ssi) { - ret = -EBUSY; - goto failed_ac97; - } - ac97_ssi = ssi; - setup_channel_to_ac97(ssi); - dai = &imx_ac97_dai; - } else - dai = &imx_ssi_dai; - - writel(0x0, ssi->base + SSI_SIER); - - ssi->dma_params_rx.dma_addr = res->start + SSI_SRX0; - ssi->dma_params_tx.dma_addr = res->start + SSI_STX0; - - ssi->dma_params_tx.burstsize = 6; - ssi->dma_params_rx.burstsize = 4; - - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0"); - if (res) - ssi->dma_params_tx.dma = res->start; - - res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0"); - if (res) - ssi->dma_params_rx.dma = res->start; - - platform_set_drvdata(pdev, ssi); - - ret = snd_soc_register_dai(&pdev->dev, dai); - if (ret) { - dev_err(&pdev->dev, "register DAI failed\n"); - goto failed_register; - } - - ssi->soc_platform_pdev_fiq = platform_device_alloc("imx-fiq-pcm-audio", pdev->id); - if (!ssi->soc_platform_pdev_fiq) { - ret = -ENOMEM; - goto failed_pdev_fiq_alloc; - } - - platform_set_drvdata(ssi->soc_platform_pdev_fiq, ssi); - ret = platform_device_add(ssi->soc_platform_pdev_fiq); - if (ret) { - dev_err(&pdev->dev, "failed to add platform device\n"); - goto failed_pdev_fiq_add; - } - - ssi->soc_platform_pdev = platform_device_alloc("imx-pcm-audio", pdev->id); - if (!ssi->soc_platform_pdev) { - ret = -ENOMEM; - goto failed_pdev_alloc; - } - - platform_set_drvdata(ssi->soc_platform_pdev, ssi); - ret = platform_device_add(ssi->soc_platform_pdev); - if (ret) { - dev_err(&pdev->dev, "failed to add platform device\n"); - goto failed_pdev_add; - } - - return 0; - -failed_pdev_add: - platform_device_put(ssi->soc_platform_pdev); -failed_pdev_alloc: - platform_device_del(ssi->soc_platform_pdev_fiq); -failed_pdev_fiq_add: - platform_device_put(ssi->soc_platform_pdev_fiq); -failed_pdev_fiq_alloc: - snd_soc_unregister_dai(&pdev->dev); -failed_register: -failed_ac97: - iounmap(ssi->base); -failed_ioremap: - release_mem_region(res->start, resource_size(res)); -failed_get_resource: - clk_disable(ssi->clk); - clk_put(ssi->clk); -failed_clk: - kfree(ssi); - - return ret; -} - -static int __devexit imx_ssi_remove(struct platform_device *pdev) -{ - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - struct imx_ssi *ssi = platform_get_drvdata(pdev); - - platform_device_unregister(ssi->soc_platform_pdev); - platform_device_unregister(ssi->soc_platform_pdev_fiq); - - snd_soc_unregister_dai(&pdev->dev); - - if (ssi->flags & IMX_SSI_USE_AC97) - ac97_ssi = NULL; - - iounmap(ssi->base); - release_mem_region(res->start, resource_size(res)); - clk_disable(ssi->clk); - clk_put(ssi->clk); - kfree(ssi); - - return 0; -} - -static struct platform_driver imx_ssi_driver = { - .probe = imx_ssi_probe, - .remove = __devexit_p(imx_ssi_remove), - - .driver = { - .name = "imx-ssi", - .owner = THIS_MODULE, - }, -}; - -module_platform_driver(imx_ssi_driver); - -/* Module information */ -MODULE_AUTHOR("Sascha Hauer, "); -MODULE_DESCRIPTION("i.MX I2S/ac97 SoC Interface"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:imx-ssi"); diff --git a/sound/soc/imx/imx-ssi.h b/sound/soc/imx/imx-ssi.h deleted file mode 100644 index 5744e86ca878..000000000000 --- a/sound/soc/imx/imx-ssi.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _IMX_SSI_H -#define _IMX_SSI_H - -#define SSI_STX0 0x00 -#define SSI_STX1 0x04 -#define SSI_SRX0 0x08 -#define SSI_SRX1 0x0c - -#define SSI_SCR 0x10 -#define SSI_SCR_CLK_IST (1 << 9) -#define SSI_SCR_CLK_IST_SHIFT 9 -#define SSI_SCR_TCH_EN (1 << 8) -#define SSI_SCR_SYS_CLK_EN (1 << 7) -#define SSI_SCR_I2S_MODE_NORM (0 << 5) -#define SSI_SCR_I2S_MODE_MSTR (1 << 5) -#define SSI_SCR_I2S_MODE_SLAVE (2 << 5) -#define SSI_I2S_MODE_MASK (3 << 5) -#define SSI_SCR_SYN (1 << 4) -#define SSI_SCR_NET (1 << 3) -#define SSI_SCR_RE (1 << 2) -#define SSI_SCR_TE (1 << 1) -#define SSI_SCR_SSIEN (1 << 0) - -#define SSI_SISR 0x14 -#define SSI_SISR_MASK ((1 << 19) - 1) -#define SSI_SISR_CMDAU (1 << 18) -#define SSI_SISR_CMDDU (1 << 17) -#define SSI_SISR_RXT (1 << 16) -#define SSI_SISR_RDR1 (1 << 15) -#define SSI_SISR_RDR0 (1 << 14) -#define SSI_SISR_TDE1 (1 << 13) -#define SSI_SISR_TDE0 (1 << 12) -#define SSI_SISR_ROE1 (1 << 11) -#define SSI_SISR_ROE0 (1 << 10) -#define SSI_SISR_TUE1 (1 << 9) -#define SSI_SISR_TUE0 (1 << 8) -#define SSI_SISR_TFS (1 << 7) -#define SSI_SISR_RFS (1 << 6) -#define SSI_SISR_TLS (1 << 5) -#define SSI_SISR_RLS (1 << 4) -#define SSI_SISR_RFF1 (1 << 3) -#define SSI_SISR_RFF0 (1 << 2) -#define SSI_SISR_TFE1 (1 << 1) -#define SSI_SISR_TFE0 (1 << 0) - -#define SSI_SIER 0x18 -#define SSI_SIER_RDMAE (1 << 22) -#define SSI_SIER_RIE (1 << 21) -#define SSI_SIER_TDMAE (1 << 20) -#define SSI_SIER_TIE (1 << 19) -#define SSI_SIER_CMDAU_EN (1 << 18) -#define SSI_SIER_CMDDU_EN (1 << 17) -#define SSI_SIER_RXT_EN (1 << 16) -#define SSI_SIER_RDR1_EN (1 << 15) -#define SSI_SIER_RDR0_EN (1 << 14) -#define SSI_SIER_TDE1_EN (1 << 13) -#define SSI_SIER_TDE0_EN (1 << 12) -#define SSI_SIER_ROE1_EN (1 << 11) -#define SSI_SIER_ROE0_EN (1 << 10) -#define SSI_SIER_TUE1_EN (1 << 9) -#define SSI_SIER_TUE0_EN (1 << 8) -#define SSI_SIER_TFS_EN (1 << 7) -#define SSI_SIER_RFS_EN (1 << 6) -#define SSI_SIER_TLS_EN (1 << 5) -#define SSI_SIER_RLS_EN (1 << 4) -#define SSI_SIER_RFF1_EN (1 << 3) -#define SSI_SIER_RFF0_EN (1 << 2) -#define SSI_SIER_TFE1_EN (1 << 1) -#define SSI_SIER_TFE0_EN (1 << 0) - -#define SSI_STCR 0x1c -#define SSI_STCR_TXBIT0 (1 << 9) -#define SSI_STCR_TFEN1 (1 << 8) -#define SSI_STCR_TFEN0 (1 << 7) -#define SSI_FIFO_ENABLE_0_SHIFT 7 -#define SSI_STCR_TFDIR (1 << 6) -#define SSI_STCR_TXDIR (1 << 5) -#define SSI_STCR_TSHFD (1 << 4) -#define SSI_STCR_TSCKP (1 << 3) -#define SSI_STCR_TFSI (1 << 2) -#define SSI_STCR_TFSL (1 << 1) -#define SSI_STCR_TEFS (1 << 0) - -#define SSI_SRCR 0x20 -#define SSI_SRCR_RXBIT0 (1 << 9) -#define SSI_SRCR_RFEN1 (1 << 8) -#define SSI_SRCR_RFEN0 (1 << 7) -#define SSI_FIFO_ENABLE_0_SHIFT 7 -#define SSI_SRCR_RFDIR (1 << 6) -#define SSI_SRCR_RXDIR (1 << 5) -#define SSI_SRCR_RSHFD (1 << 4) -#define SSI_SRCR_RSCKP (1 << 3) -#define SSI_SRCR_RFSI (1 << 2) -#define SSI_SRCR_RFSL (1 << 1) -#define SSI_SRCR_REFS (1 << 0) - -#define SSI_SRCCR 0x28 -#define SSI_SRCCR_DIV2 (1 << 18) -#define SSI_SRCCR_PSR (1 << 17) -#define SSI_SRCCR_WL(x) ((((x) - 2) >> 1) << 13) -#define SSI_SRCCR_DC(x) (((x) & 0x1f) << 8) -#define SSI_SRCCR_PM(x) (((x) & 0xff) << 0) -#define SSI_SRCCR_WL_MASK (0xf << 13) -#define SSI_SRCCR_DC_MASK (0x1f << 8) -#define SSI_SRCCR_PM_MASK (0xff << 0) - -#define SSI_STCCR 0x24 -#define SSI_STCCR_DIV2 (1 << 18) -#define SSI_STCCR_PSR (1 << 17) -#define SSI_STCCR_WL(x) ((((x) - 2) >> 1) << 13) -#define SSI_STCCR_DC(x) (((x) & 0x1f) << 8) -#define SSI_STCCR_PM(x) (((x) & 0xff) << 0) -#define SSI_STCCR_WL_MASK (0xf << 13) -#define SSI_STCCR_DC_MASK (0x1f << 8) -#define SSI_STCCR_PM_MASK (0xff << 0) - -#define SSI_SFCSR 0x2c -#define SSI_SFCSR_RFCNT1(x) (((x) & 0xf) << 28) -#define SSI_RX_FIFO_1_COUNT_SHIFT 28 -#define SSI_SFCSR_TFCNT1(x) (((x) & 0xf) << 24) -#define SSI_TX_FIFO_1_COUNT_SHIFT 24 -#define SSI_SFCSR_RFWM1(x) (((x) & 0xf) << 20) -#define SSI_SFCSR_TFWM1(x) (((x) & 0xf) << 16) -#define SSI_SFCSR_RFCNT0(x) (((x) & 0xf) << 12) -#define SSI_RX_FIFO_0_COUNT_SHIFT 12 -#define SSI_SFCSR_TFCNT0(x) (((x) & 0xf) << 8) -#define SSI_TX_FIFO_0_COUNT_SHIFT 8 -#define SSI_SFCSR_RFWM0(x) (((x) & 0xf) << 4) -#define SSI_SFCSR_TFWM0(x) (((x) & 0xf) << 0) -#define SSI_SFCSR_RFWM0_MASK (0xf << 4) -#define SSI_SFCSR_TFWM0_MASK (0xf << 0) - -#define SSI_STR 0x30 -#define SSI_STR_TEST (1 << 15) -#define SSI_STR_RCK2TCK (1 << 14) -#define SSI_STR_RFS2TFS (1 << 13) -#define SSI_STR_RXSTATE(x) (((x) & 0xf) << 8) -#define SSI_STR_TXD2RXD (1 << 7) -#define SSI_STR_TCK2RCK (1 << 6) -#define SSI_STR_TFS2RFS (1 << 5) -#define SSI_STR_TXSTATE(x) (((x) & 0xf) << 0) - -#define SSI_SOR 0x34 -#define SSI_SOR_CLKOFF (1 << 6) -#define SSI_SOR_RX_CLR (1 << 5) -#define SSI_SOR_TX_CLR (1 << 4) -#define SSI_SOR_INIT (1 << 3) -#define SSI_SOR_WAIT(x) (((x) & 0x3) << 1) -#define SSI_SOR_WAIT_MASK (0x3 << 1) -#define SSI_SOR_SYNRST (1 << 0) - -#define SSI_SACNT 0x38 -#define SSI_SACNT_FRDIV(x) (((x) & 0x3f) << 5) -#define SSI_SACNT_WR (1 << 4) -#define SSI_SACNT_RD (1 << 3) -#define SSI_SACNT_TIF (1 << 2) -#define SSI_SACNT_FV (1 << 1) -#define SSI_SACNT_AC97EN (1 << 0) - -#define SSI_SACADD 0x3c -#define SSI_SACDAT 0x40 -#define SSI_SATAG 0x44 -#define SSI_STMSK 0x48 -#define SSI_SRMSK 0x4c -#define SSI_SACCST 0x50 -#define SSI_SACCEN 0x54 -#define SSI_SACCDIS 0x58 - -/* SSI clock sources */ -#define IMX_SSP_SYS_CLK 0 - -/* SSI audio dividers */ -#define IMX_SSI_TX_DIV_2 0 -#define IMX_SSI_TX_DIV_PSR 1 -#define IMX_SSI_TX_DIV_PM 2 -#define IMX_SSI_RX_DIV_2 3 -#define IMX_SSI_RX_DIV_PSR 4 -#define IMX_SSI_RX_DIV_PM 5 - -#define DRV_NAME "imx-ssi" - -#include -#include -#include "imx-pcm.h" - -struct imx_ssi { - struct platform_device *ac97_dev; - - struct snd_soc_dai *imx_ac97; - struct clk *clk; - void __iomem *base; - int irq; - int fiq_enable; - unsigned int offset; - - unsigned int flags; - - void (*ac97_reset) (struct snd_ac97 *ac97); - void (*ac97_warm_reset)(struct snd_ac97 *ac97); - - struct imx_pcm_dma_params dma_params_rx; - struct imx_pcm_dma_params dma_params_tx; - - int enabled; - - struct platform_device *soc_platform_pdev; - struct platform_device *soc_platform_pdev_fiq; -}; - -#endif /* _IMX_SSI_H */ diff --git a/sound/soc/imx/mx27vis-aic32x4.c b/sound/soc/imx/mx27vis-aic32x4.c deleted file mode 100644 index f6d04ad4bb39..000000000000 --- a/sound/soc/imx/mx27vis-aic32x4.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * mx27vis-aic32x4.c - * - * Copyright 2011 Vista Silicon S.L. - * - * Author: Javier Martin - * - * 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; either version 2 of the License, or (at your - * option) any later version. - * - * 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., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../codecs/tlv320aic32x4.h" -#include "imx-ssi.h" -#include "imx-audmux.h" - -#define MX27VIS_AMP_GAIN 0 -#define MX27VIS_AMP_MUTE 1 - -#define MX27VIS_PIN_G0 (GPIO_PORTF + 9) -#define MX27VIS_PIN_G1 (GPIO_PORTF + 8) -#define MX27VIS_PIN_SDL (GPIO_PORTE + 5) -#define MX27VIS_PIN_SDR (GPIO_PORTF + 7) - -static int mx27vis_amp_gain; -static int mx27vis_amp_mute; - -static const int mx27vis_amp_pins[] = { - MX27VIS_PIN_G0 | GPIO_GPIO | GPIO_OUT, - MX27VIS_PIN_G1 | GPIO_GPIO | GPIO_OUT, - MX27VIS_PIN_SDL | GPIO_GPIO | GPIO_OUT, - MX27VIS_PIN_SDR | GPIO_GPIO | GPIO_OUT, -}; - -static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int ret; - u32 dai_format; - - dai_format = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM; - - /* set codec DAI configuration */ - snd_soc_dai_set_fmt(codec_dai, dai_format); - - /* set cpu DAI configuration */ - snd_soc_dai_set_fmt(cpu_dai, dai_format); - - ret = snd_soc_dai_set_sysclk(codec_dai, 0, - 25000000, SND_SOC_CLOCK_OUT); - if (ret) { - pr_err("%s: failed setting codec sysclk\n", __func__); - return ret; - } - - ret = snd_soc_dai_set_sysclk(cpu_dai, IMX_SSP_SYS_CLK, 0, - SND_SOC_CLOCK_IN); - if (ret) { - pr_err("can't set CPU system clock IMX_SSP_SYS_CLK\n"); - return ret; - } - - return 0; -} - -static struct snd_soc_ops mx27vis_aic32x4_snd_ops = { - .hw_params = mx27vis_aic32x4_hw_params, -}; - -static int mx27vis_amp_set(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int value = ucontrol->value.integer.value[0]; - unsigned int reg = mc->reg; - int max = mc->max; - - if (value > max) - return -EINVAL; - - switch (reg) { - case MX27VIS_AMP_GAIN: - gpio_set_value(MX27VIS_PIN_G0, value & 1); - gpio_set_value(MX27VIS_PIN_G1, value >> 1); - mx27vis_amp_gain = value; - break; - case MX27VIS_AMP_MUTE: - gpio_set_value(MX27VIS_PIN_SDL, value & 1); - gpio_set_value(MX27VIS_PIN_SDR, value >> 1); - mx27vis_amp_mute = value; - break; - } - return 0; -} - -static int mx27vis_amp_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - unsigned int reg = mc->reg; - - switch (reg) { - case MX27VIS_AMP_GAIN: - ucontrol->value.integer.value[0] = mx27vis_amp_gain; - break; - case MX27VIS_AMP_MUTE: - ucontrol->value.integer.value[0] = mx27vis_amp_mute; - break; - } - return 0; -} - -/* From 6dB to 24dB in steps of 6dB */ -static const DECLARE_TLV_DB_SCALE(mx27vis_amp_tlv, 600, 600, 0); - -static const struct snd_kcontrol_new mx27vis_aic32x4_controls[] = { - SOC_DAPM_PIN_SWITCH("External Mic"), - SOC_SINGLE_EXT_TLV("LO Ext Boost", MX27VIS_AMP_GAIN, 0, 3, 0, - mx27vis_amp_get, mx27vis_amp_set, mx27vis_amp_tlv), - SOC_DOUBLE_EXT("LO Ext Mute Switch", MX27VIS_AMP_MUTE, 0, 1, 1, 0, - mx27vis_amp_get, mx27vis_amp_set), -}; - -static const struct snd_soc_dapm_widget aic32x4_dapm_widgets[] = { - SND_SOC_DAPM_MIC("External Mic", NULL), -}; - -static const struct snd_soc_dapm_route aic32x4_dapm_routes[] = { - {"Mic Bias", NULL, "External Mic"}, - {"IN1_R", NULL, "Mic Bias"}, - {"IN2_R", NULL, "Mic Bias"}, - {"IN3_R", NULL, "Mic Bias"}, - {"IN1_L", NULL, "Mic Bias"}, - {"IN2_L", NULL, "Mic Bias"}, - {"IN3_L", NULL, "Mic Bias"}, -}; - -static struct snd_soc_dai_link mx27vis_aic32x4_dai = { - .name = "tlv320aic32x4", - .stream_name = "TLV320AIC32X4", - .codec_dai_name = "tlv320aic32x4-hifi", - .platform_name = "imx-pcm-audio.0", - .codec_name = "tlv320aic32x4.0-0018", - .cpu_dai_name = "imx-ssi.0", - .ops = &mx27vis_aic32x4_snd_ops, -}; - -static struct snd_soc_card mx27vis_aic32x4 = { - .name = "visstrim_m10-audio", - .owner = THIS_MODULE, - .dai_link = &mx27vis_aic32x4_dai, - .num_links = 1, - .controls = mx27vis_aic32x4_controls, - .num_controls = ARRAY_SIZE(mx27vis_aic32x4_controls), - .dapm_widgets = aic32x4_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets), - .dapm_routes = aic32x4_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes), -}; - -static int __devinit mx27vis_aic32x4_probe(struct platform_device *pdev) -{ - int ret; - - mx27vis_aic32x4.dev = &pdev->dev; - ret = snd_soc_register_card(&mx27vis_aic32x4); - if (ret) { - dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", - ret); - return ret; - } - - /* Connect SSI0 as clock slave to SSI1 external pins */ - imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, - IMX_AUDMUX_V1_PCR_SYN | - IMX_AUDMUX_V1_PCR_TFSDIR | - IMX_AUDMUX_V1_PCR_TCLKDIR | - IMX_AUDMUX_V1_PCR_TFCSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) | - IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_PPCR1_SSI_PINS_1) - ); - imx_audmux_v1_configure_port(MX27_AUDMUX_PPCR1_SSI_PINS_1, - IMX_AUDMUX_V1_PCR_SYN | - IMX_AUDMUX_V1_PCR_RXDSEL(MX27_AUDMUX_HPCR1_SSI0) - ); - - ret = mxc_gpio_setup_multiple_pins(mx27vis_amp_pins, - ARRAY_SIZE(mx27vis_amp_pins), "MX27VIS_AMP"); - if (ret) - printk(KERN_ERR "ASoC: unable to setup gpios\n"); - - return ret; -} - -static int __devexit mx27vis_aic32x4_remove(struct platform_device *pdev) -{ - snd_soc_unregister_card(&mx27vis_aic32x4); - - return 0; -} - -static struct platform_driver mx27vis_aic32x4_audio_driver = { - .driver = { - .name = "mx27vis", - .owner = THIS_MODULE, - }, - .probe = mx27vis_aic32x4_probe, - .remove = __devexit_p(mx27vis_aic32x4_remove), -}; - -module_platform_driver(mx27vis_aic32x4_audio_driver); - -MODULE_AUTHOR("Javier Martin "); -MODULE_DESCRIPTION("ALSA SoC AIC32X4 mx27 visstrim"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:mx27vis"); diff --git a/sound/soc/imx/phycore-ac97.c b/sound/soc/imx/phycore-ac97.c deleted file mode 100644 index f8da6dd115ed..000000000000 --- a/sound/soc/imx/phycore-ac97.c +++ /dev/null @@ -1,125 +0,0 @@ -/* - * phycore-ac97.c -- SoC audio for imx_phycore in AC97 mode - * - * Copyright 2009 Sascha Hauer, Pengutronix - * - * 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; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "imx-audmux.h" - -static struct snd_soc_card imx_phycore; - -static struct snd_soc_ops imx_phycore_hifi_ops = { -}; - -static struct snd_soc_dai_link imx_phycore_dai_ac97[] = { - { - .name = "HiFi", - .stream_name = "HiFi", - .codec_dai_name = "wm9712-hifi", - .codec_name = "wm9712-codec", - .cpu_dai_name = "imx-ssi.0", - .platform_name = "imx-fiq-pcm-audio.0", - .ops = &imx_phycore_hifi_ops, - }, -}; - -static struct snd_soc_card imx_phycore = { - .name = "PhyCORE-ac97-audio", - .owner = THIS_MODULE, - .dai_link = imx_phycore_dai_ac97, - .num_links = ARRAY_SIZE(imx_phycore_dai_ac97), -}; - -static struct platform_device *imx_phycore_snd_ac97_device; -static struct platform_device *imx_phycore_snd_device; - -static int __init imx_phycore_init(void) -{ - int ret; - - if (machine_is_pca100()) { - imx_audmux_v1_configure_port(MX27_AUDMUX_HPCR1_SSI0, - IMX_AUDMUX_V1_PCR_SYN | /* 4wire mode */ - IMX_AUDMUX_V1_PCR_TFCSEL(3) | - IMX_AUDMUX_V1_PCR_TCLKDIR | /* clock is output */ - IMX_AUDMUX_V1_PCR_RXDSEL(3)); - imx_audmux_v1_configure_port(3, - IMX_AUDMUX_V1_PCR_SYN | /* 4wire mode */ - IMX_AUDMUX_V1_PCR_TFCSEL(0) | - IMX_AUDMUX_V1_PCR_TFSDIR | - IMX_AUDMUX_V1_PCR_RXDSEL(0)); - } else if (machine_is_pcm043()) { - imx_audmux_v2_configure_port(3, - IMX_AUDMUX_V2_PTCR_SYN | /* 4wire mode */ - IMX_AUDMUX_V2_PTCR_TFSEL(0) | - IMX_AUDMUX_V2_PTCR_TFSDIR, - IMX_AUDMUX_V2_PDCR_RXDSEL(0)); - imx_audmux_v2_configure_port(0, - IMX_AUDMUX_V2_PTCR_SYN | /* 4wire mode */ - IMX_AUDMUX_V2_PTCR_TCSEL(3) | - IMX_AUDMUX_V2_PTCR_TCLKDIR, /* clock is output */ - IMX_AUDMUX_V2_PDCR_RXDSEL(3)); - } else { - /* return happy. We might run on a totally different machine */ - return 0; - } - - imx_phycore_snd_ac97_device = platform_device_alloc("soc-audio", -1); - if (!imx_phycore_snd_ac97_device) - return -ENOMEM; - - platform_set_drvdata(imx_phycore_snd_ac97_device, &imx_phycore); - ret = platform_device_add(imx_phycore_snd_ac97_device); - if (ret) - goto fail1; - - imx_phycore_snd_device = platform_device_alloc("wm9712-codec", -1); - if (!imx_phycore_snd_device) { - ret = -ENOMEM; - goto fail2; - } - ret = platform_device_add(imx_phycore_snd_device); - - if (ret) { - printk(KERN_ERR "ASoC: Platform device allocation failed\n"); - goto fail3; - } - - return 0; - -fail3: - platform_device_put(imx_phycore_snd_device); -fail2: - platform_device_del(imx_phycore_snd_ac97_device); -fail1: - platform_device_put(imx_phycore_snd_ac97_device); - return ret; -} - -static void __exit imx_phycore_exit(void) -{ - platform_device_unregister(imx_phycore_snd_device); - platform_device_unregister(imx_phycore_snd_ac97_device); -} - -late_initcall(imx_phycore_init); -module_exit(imx_phycore_exit); - -MODULE_AUTHOR("Sascha Hauer "); -MODULE_DESCRIPTION("PhyCORE ALSA SoC driver"); -MODULE_LICENSE("GPL"); diff --git a/sound/soc/imx/wm1133-ev1.c b/sound/soc/imx/wm1133-ev1.c deleted file mode 100644 index fe54a69073e5..000000000000 --- a/sound/soc/imx/wm1133-ev1.c +++ /dev/null @@ -1,304 +0,0 @@ -/* - * wm1133-ev1.c - Audio for WM1133-EV1 on i.MX31ADS - * - * Copyright (c) 2010 Wolfson Microelectronics plc - * Author: Mark Brown - * - * Based on an earlier driver for the same hardware by Liam Girdwood. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "imx-ssi.h" -#include "../codecs/wm8350.h" -#include "imx-audmux.h" - -/* There is a silicon mic on the board optionally connected via a solder pad - * SP1. Define this to enable it. - */ -#undef USE_SIMIC - -struct _wm8350_audio { - unsigned int channels; - snd_pcm_format_t format; - unsigned int rate; - unsigned int sysclk; - unsigned int bclkdiv; - unsigned int clkdiv; - unsigned int lr_rate; -}; - -/* in order of power consumption per rate (lowest first) */ -static const struct _wm8350_audio wm8350_audio[] = { - /* 16bit mono modes */ - {1, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000 >> 1, - WM8350_BCLK_DIV_48, WM8350_DACDIV_3, 16,}, - - /* 16 bit stereo modes */ - {2, SNDRV_PCM_FORMAT_S16_LE, 8000, 12288000, - WM8350_BCLK_DIV_48, WM8350_DACDIV_6, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 16000, 12288000, - WM8350_BCLK_DIV_24, WM8350_DACDIV_3, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 32000, 12288000, - WM8350_BCLK_DIV_12, WM8350_DACDIV_1_5, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 48000, 12288000, - WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 96000, 24576000, - WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 11025, 11289600, - WM8350_BCLK_DIV_32, WM8350_DACDIV_4, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 22050, 11289600, - WM8350_BCLK_DIV_16, WM8350_DACDIV_2, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 44100, 11289600, - WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,}, - {2, SNDRV_PCM_FORMAT_S16_LE, 88200, 22579200, - WM8350_BCLK_DIV_8, WM8350_DACDIV_1, 32,}, - - /* 24bit stereo modes */ - {2, SNDRV_PCM_FORMAT_S24_LE, 48000, 12288000, - WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,}, - {2, SNDRV_PCM_FORMAT_S24_LE, 96000, 24576000, - WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,}, - {2, SNDRV_PCM_FORMAT_S24_LE, 44100, 11289600, - WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,}, - {2, SNDRV_PCM_FORMAT_S24_LE, 88200, 22579200, - WM8350_BCLK_DIV_4, WM8350_DACDIV_1, 64,}, -}; - -static int wm1133_ev1_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params) -{ - struct snd_soc_pcm_runtime *rtd = substream->private_data; - struct snd_soc_dai *codec_dai = rtd->codec_dai; - struct snd_soc_dai *cpu_dai = rtd->cpu_dai; - int i, found = 0; - snd_pcm_format_t format = params_format(params); - unsigned int rate = params_rate(params); - unsigned int channels = params_channels(params); - u32 dai_format; - - /* find the correct audio parameters */ - for (i = 0; i < ARRAY_SIZE(wm8350_audio); i++) { - if (rate == wm8350_audio[i].rate && - format == wm8350_audio[i].format && - channels == wm8350_audio[i].channels) { - found = 1; - break; - } - } - if (!found) - return -EINVAL; - - /* codec FLL input is 14.75 MHz from MCLK */ - snd_soc_dai_set_pll(codec_dai, 0, 0, 14750000, wm8350_audio[i].sysclk); - - dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | - SND_SOC_DAIFMT_CBM_CFM; - - /* set codec DAI configuration */ - snd_soc_dai_set_fmt(codec_dai, dai_format); - - /* set cpu DAI configuration */ - snd_soc_dai_set_fmt(cpu_dai, dai_format); - - /* TODO: The SSI driver should figure this out for us */ - switch (channels) { - case 2: - snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffc, 0xffffffc, 2, 0); - break; - case 1: - snd_soc_dai_set_tdm_slot(cpu_dai, 0xffffffe, 0xffffffe, 1, 0); - break; - default: - return -EINVAL; - } - - /* set MCLK as the codec system clock for DAC and ADC */ - snd_soc_dai_set_sysclk(codec_dai, WM8350_MCLK_SEL_PLL_MCLK, - wm8350_audio[i].sysclk, SND_SOC_CLOCK_IN); - - /* set codec BCLK division for sample rate */ - snd_soc_dai_set_clkdiv(codec_dai, WM8350_BCLK_CLKDIV, - wm8350_audio[i].bclkdiv); - - /* DAI is synchronous and clocked with DAC LRCLK & ADC LRC */ - snd_soc_dai_set_clkdiv(codec_dai, - WM8350_DACLR_CLKDIV, wm8350_audio[i].lr_rate); - snd_soc_dai_set_clkdiv(codec_dai, - WM8350_ADCLR_CLKDIV, wm8350_audio[i].lr_rate); - - /* now configure DAC and ADC clocks */ - snd_soc_dai_set_clkdiv(codec_dai, - WM8350_DAC_CLKDIV, wm8350_audio[i].clkdiv); - - snd_soc_dai_set_clkdiv(codec_dai, - WM8350_ADC_CLKDIV, wm8350_audio[i].clkdiv); - - return 0; -} - -static struct snd_soc_ops wm1133_ev1_ops = { - .hw_params = wm1133_ev1_hw_params, -}; - -static const struct snd_soc_dapm_widget wm1133_ev1_widgets[] = { -#ifdef USE_SIMIC - SND_SOC_DAPM_MIC("SiMIC", NULL), -#endif - SND_SOC_DAPM_MIC("Mic1 Jack", NULL), - SND_SOC_DAPM_MIC("Mic2 Jack", NULL), - SND_SOC_DAPM_LINE("Line In Jack", NULL), - SND_SOC_DAPM_LINE("Line Out Jack", NULL), - SND_SOC_DAPM_HP("Headphone Jack", NULL), -}; - -/* imx32ads soc_card audio map */ -static const struct snd_soc_dapm_route wm1133_ev1_map[] = { - -#ifdef USE_SIMIC - /* SiMIC --> IN1LN (with automatic bias) via SP1 */ - { "IN1LN", NULL, "Mic Bias" }, - { "Mic Bias", NULL, "SiMIC" }, -#endif - - /* Mic 1 Jack --> IN1LN and IN1LP (with automatic bias) */ - { "IN1LN", NULL, "Mic Bias" }, - { "IN1LP", NULL, "Mic1 Jack" }, - { "Mic Bias", NULL, "Mic1 Jack" }, - - /* Mic 2 Jack --> IN1RN and IN1RP (with automatic bias) */ - { "IN1RN", NULL, "Mic Bias" }, - { "IN1RP", NULL, "Mic2 Jack" }, - { "Mic Bias", NULL, "Mic2 Jack" }, - - /* Line in Jack --> AUX (L+R) */ - { "IN3R", NULL, "Line In Jack" }, - { "IN3L", NULL, "Line In Jack" }, - - /* Out1 --> Headphone Jack */ - { "Headphone Jack", NULL, "OUT1R" }, - { "Headphone Jack", NULL, "OUT1L" }, - - /* Out1 --> Line Out Jack */ - { "Line Out Jack", NULL, "OUT2R" }, - { "Line Out Jack", NULL, "OUT2L" }, -}; - -static struct snd_soc_jack hp_jack; - -static struct snd_soc_jack_pin hp_jack_pins[] = { - { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE }, -}; - -static struct snd_soc_jack mic_jack; - -static struct snd_soc_jack_pin mic_jack_pins[] = { - { .pin = "Mic1 Jack", .mask = SND_JACK_MICROPHONE }, - { .pin = "Mic2 Jack", .mask = SND_JACK_MICROPHONE }, -}; - -static int wm1133_ev1_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_codec *codec = rtd->codec; - struct snd_soc_dapm_context *dapm = &codec->dapm; - - snd_soc_dapm_new_controls(dapm, wm1133_ev1_widgets, - ARRAY_SIZE(wm1133_ev1_widgets)); - - snd_soc_dapm_add_routes(dapm, wm1133_ev1_map, - ARRAY_SIZE(wm1133_ev1_map)); - - /* Headphone jack detection */ - snd_soc_jack_new(codec, "Headphone", SND_JACK_HEADPHONE, &hp_jack); - snd_soc_jack_add_pins(&hp_jack, ARRAY_SIZE(hp_jack_pins), - hp_jack_pins); - wm8350_hp_jack_detect(codec, WM8350_JDR, &hp_jack, SND_JACK_HEADPHONE); - - /* Microphone jack detection */ - snd_soc_jack_new(codec, "Microphone", - SND_JACK_MICROPHONE | SND_JACK_BTN_0, &mic_jack); - snd_soc_jack_add_pins(&mic_jack, ARRAY_SIZE(mic_jack_pins), - mic_jack_pins); - wm8350_mic_jack_detect(codec, &mic_jack, SND_JACK_MICROPHONE, - SND_JACK_BTN_0); - - snd_soc_dapm_force_enable_pin(dapm, "Mic Bias"); - - return 0; -} - - -static struct snd_soc_dai_link wm1133_ev1_dai = { - .name = "WM1133-EV1", - .stream_name = "Audio", - .cpu_dai_name = "imx-ssi.0", - .codec_dai_name = "wm8350-hifi", - .platform_name = "imx-fiq-pcm-audio.0", - .codec_name = "wm8350-codec.0-0x1a", - .init = wm1133_ev1_init, - .ops = &wm1133_ev1_ops, - .symmetric_rates = 1, -}; - -static struct snd_soc_card wm1133_ev1 = { - .name = "WM1133-EV1", - .owner = THIS_MODULE, - .dai_link = &wm1133_ev1_dai, - .num_links = 1, -}; - -static struct platform_device *wm1133_ev1_snd_device; - -static int __init wm1133_ev1_audio_init(void) -{ - int ret; - unsigned int ptcr, pdcr; - - /* SSI0 mastered by port 5 */ - ptcr = IMX_AUDMUX_V2_PTCR_SYN | - IMX_AUDMUX_V2_PTCR_TFSDIR | - IMX_AUDMUX_V2_PTCR_TFSEL(MX31_AUDMUX_PORT5_SSI_PINS_5) | - IMX_AUDMUX_V2_PTCR_TCLKDIR | - IMX_AUDMUX_V2_PTCR_TCSEL(MX31_AUDMUX_PORT5_SSI_PINS_5); - pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT5_SSI_PINS_5); - imx_audmux_v2_configure_port(MX31_AUDMUX_PORT1_SSI0, ptcr, pdcr); - - ptcr = IMX_AUDMUX_V2_PTCR_SYN; - pdcr = IMX_AUDMUX_V2_PDCR_RXDSEL(MX31_AUDMUX_PORT1_SSI0); - imx_audmux_v2_configure_port(MX31_AUDMUX_PORT5_SSI_PINS_5, ptcr, pdcr); - - wm1133_ev1_snd_device = platform_device_alloc("soc-audio", -1); - if (!wm1133_ev1_snd_device) - return -ENOMEM; - - platform_set_drvdata(wm1133_ev1_snd_device, &wm1133_ev1); - ret = platform_device_add(wm1133_ev1_snd_device); - - if (ret) - platform_device_put(wm1133_ev1_snd_device); - - return ret; -} -module_init(wm1133_ev1_audio_init); - -static void __exit wm1133_ev1_audio_exit(void) -{ - platform_device_unregister(wm1133_ev1_snd_device); -} -module_exit(wm1133_ev1_audio_exit); - -MODULE_AUTHOR("Mark Brown "); -MODULE_DESCRIPTION("Audio for WM1133-EV1 on i.MX31ADS"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From ff9062c60e4b8e01980b1a81dcabe3965f71df77 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Tue, 20 Mar 2012 14:55:50 -0600 Subject: ARM: tegra: remove tegra_pcm_device tegra_pcm_device is no longer needed now that the Tegra ASoC code has cleaned up its 'platform' registration. Remove it. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- arch/arm/mach-tegra/board-harmony.c | 1 - arch/arm/mach-tegra/board-seaboard.c | 1 - arch/arm/mach-tegra/board-trimslice.c | 1 - arch/arm/mach-tegra/devices.c | 5 ----- arch/arm/mach-tegra/devices.h | 1 - 5 files changed, 9 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-tegra/board-harmony.c b/arch/arm/mach-tegra/board-harmony.c index c00aadb01e09..5eb74ea98141 100644 --- a/arch/arm/mach-tegra/board-harmony.c +++ b/arch/arm/mach-tegra/board-harmony.c @@ -122,7 +122,6 @@ static struct platform_device *harmony_devices[] __initdata = { &tegra_ehci3_device, &tegra_i2s_device1, &tegra_das_device, - &tegra_pcm_device, &harmony_audio_device, }; diff --git a/arch/arm/mach-tegra/board-seaboard.c b/arch/arm/mach-tegra/board-seaboard.c index d669847f0485..0e2957f90201 100644 --- a/arch/arm/mach-tegra/board-seaboard.c +++ b/arch/arm/mach-tegra/board-seaboard.c @@ -153,7 +153,6 @@ static struct platform_device *seaboard_devices[] __initdata = { &seaboard_gpio_keys_device, &tegra_i2s_device1, &tegra_das_device, - &tegra_pcm_device, &seaboard_audio_device, }; diff --git a/arch/arm/mach-tegra/board-trimslice.c b/arch/arm/mach-tegra/board-trimslice.c index cd52820a3e37..ba2e04756c5e 100644 --- a/arch/arm/mach-tegra/board-trimslice.c +++ b/arch/arm/mach-tegra/board-trimslice.c @@ -86,7 +86,6 @@ static struct platform_device *trimslice_devices[] __initdata = { &tegra_sdhci_device4, &tegra_i2s_device1, &tegra_das_device, - &tegra_pcm_device, &trimslice_audio_device, }; diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c index 5f6b867e20b4..c3b990097f28 100644 --- a/arch/arm/mach-tegra/devices.c +++ b/arch/arm/mach-tegra/devices.c @@ -698,8 +698,3 @@ struct platform_device tegra_das_device = { .num_resources = ARRAY_SIZE(tegra_das_resources), .resource = tegra_das_resources, }; - -struct platform_device tegra_pcm_device = { - .name = "tegra-pcm-audio", - .id = -1, -}; diff --git a/arch/arm/mach-tegra/devices.h b/arch/arm/mach-tegra/devices.h index ec455679b219..138c642e59f4 100644 --- a/arch/arm/mach-tegra/devices.h +++ b/arch/arm/mach-tegra/devices.h @@ -52,6 +52,5 @@ extern struct platform_device tegra_pmu_device; extern struct platform_device tegra_i2s_device1; extern struct platform_device tegra_i2s_device2; extern struct platform_device tegra_das_device; -extern struct platform_device tegra_pcm_device; #endif -- cgit v1.2.3 From d5be0d73ab8655e785ae49e4f19bf031a938b336 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 29 Mar 2012 21:58:58 +0200 Subject: ARM: imx/mx21ads: remove unneeded cast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Uwe Kleine-König Signed-off-by: Sascha Hauer --- arch/arm/mach-imx/mach-mx21ads.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-imx/mach-mx21ads.c b/arch/arm/mach-imx/mach-mx21ads.c index e432d4acee1f..d14bbe949a4f 100644 --- a/arch/arm/mach-imx/mach-mx21ads.c +++ b/arch/arm/mach-imx/mach-mx21ads.c @@ -304,8 +304,7 @@ static void __init mx21ads_board_init(void) imx21_add_mxc_nand(&mx21ads_nand_board_info); platform_add_devices(platform_devices, ARRAY_SIZE(platform_devices)); - platform_device_register_full( - (struct platform_device_info *)&mx21ads_cs8900_devinfo); + platform_device_register_full(&mx21ads_cs8900_devinfo); } static void __init mx21ads_timer_init(void) -- cgit v1.2.3 From 06e44840368efdb359224b5d04db6c92f73bd373 Mon Sep 17 00:00:00 2001 From: Jesper Nilsson Date: Fri, 1 Apr 2011 17:13:17 +0200 Subject: CRISv32: Correct name of read_mostly section. 54cb27a71f51d304342c79e62fd7667f2171062b renamed .data.read_mostly to .data..read_mostly for all architectures for 2.6.33. Reported-by: Ralf Baechle Signed-off-by: Jesper Nilsson --- arch/cris/include/arch-v32/arch/cache.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/cris/include/arch-v32/arch/cache.h b/arch/cris/include/arch-v32/arch/cache.h index 1de779f4f240..7caf25d58e6b 100644 --- a/arch/cris/include/arch-v32/arch/cache.h +++ b/arch/cris/include/arch-v32/arch/cache.h @@ -7,7 +7,7 @@ #define L1_CACHE_BYTES 32 #define L1_CACHE_SHIFT 5 -#define __read_mostly __attribute__((__section__(".data.read_mostly"))) +#define __read_mostly __attribute__((__section__(".data..read_mostly"))) void flush_dma_list(dma_descr_data *descr); void flush_dma_descr(dma_descr_data *descr, int flush_buf); -- cgit v1.2.3 From 4bade0481cc378eee8f1f9412dc10dcc1b401f23 Mon Sep 17 00:00:00 2001 From: Wanlong Gao Date: Sun, 10 Apr 2011 16:10:25 +0200 Subject: cris:removed the unused variable removed the unused variable "unsigned long tmp". Signed-off-by: Wanlong Gao Signed-off-by: Jesper Nilsson --- arch/cris/arch-v32/kernel/ptrace.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'arch') diff --git a/arch/cris/arch-v32/kernel/ptrace.c b/arch/cris/arch-v32/kernel/ptrace.c index f7ad9e8637df..f085229cf870 100644 --- a/arch/cris/arch-v32/kernel/ptrace.c +++ b/arch/cris/arch-v32/kernel/ptrace.c @@ -114,8 +114,6 @@ void user_disable_single_step(struct task_struct *child) void ptrace_disable(struct task_struct *child) { - unsigned long tmp; - /* Deconfigure SPC and S-bit. */ user_disable_single_step(child); put_reg(child, PT_SPC, 0); -- cgit v1.2.3 From a52bdec35b9431f1b6939b35c31e04b2fd57e2f2 Mon Sep 17 00:00:00 2001 From: Jesper Nilsson Date: Tue, 12 Apr 2011 10:50:19 +0200 Subject: cris/arch-v32: cryptocop: Use kzalloc This avoids unnecessary explicit initialization by allocating zeroed memory. Signed-off-by: Maxin B. John Signed-off-by: Jesper Nilsson --- arch/cris/arch-v32/drivers/cryptocop.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'arch') diff --git a/arch/cris/arch-v32/drivers/cryptocop.c b/arch/cris/arch-v32/drivers/cryptocop.c index 642c6fed43d7..f8476d9e856b 100644 --- a/arch/cris/arch-v32/drivers/cryptocop.c +++ b/arch/cris/arch-v32/drivers/cryptocop.c @@ -1394,11 +1394,10 @@ static int create_md5_pad(int alloc_flag, unsigned long long hashed_length, char if (padlen < MD5_MIN_PAD_LENGTH) padlen += MD5_BLOCK_LENGTH; - p = kmalloc(padlen, alloc_flag); + p = kzalloc(padlen, alloc_flag); if (!p) return -ENOMEM; *p = 0x80; - memset(p+1, 0, padlen - 1); DEBUG(printk("create_md5_pad: hashed_length=%lld bits == %lld bytes\n", bit_length, hashed_length)); @@ -1426,11 +1425,10 @@ static int create_sha1_pad(int alloc_flag, unsigned long long hashed_length, cha if (padlen < SHA1_MIN_PAD_LENGTH) padlen += SHA1_BLOCK_LENGTH; - p = kmalloc(padlen, alloc_flag); + p = kzalloc(padlen, alloc_flag); if (!p) return -ENOMEM; *p = 0x80; - memset(p+1, 0, padlen - 1); DEBUG(printk("create_sha1_pad: hashed_length=%lld bits == %lld bytes\n", bit_length, hashed_length)); -- cgit v1.2.3 From 74f077d2a7651409c44bb323471f219a4b0d2aab Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Mon, 2 Apr 2012 13:40:17 +0200 Subject: cris: posix_types.h, include asm-generic/posix_types.h Without that I cannot build anything: In file included from include/linux/page-flags.h:8:0, from kernel/bounds.c:9: include/linux/types.h:25:1: error: unknown type name '__kernel_ino_t' include/linux/types.h:29:1: error: unknown type name '__kernel_off_t' ... Signed-off-by: Jiri Slaby Cc: Mikael Starvik Signed-off-by: Jesper Nilsson Cc: linux-cris-kernel@axis.com --- arch/cris/include/asm/posix_types.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'arch') diff --git a/arch/cris/include/asm/posix_types.h b/arch/cris/include/asm/posix_types.h index 72b3cd6eda0b..234891c74e2b 100644 --- a/arch/cris/include/asm/posix_types.h +++ b/arch/cris/include/asm/posix_types.h @@ -33,4 +33,6 @@ typedef int __kernel_ptrdiff_t; typedef unsigned short __kernel_old_dev_t; #define __kernel_old_dev_t __kernel_old_dev_t +#include + #endif /* __ARCH_CRIS_POSIX_TYPES_H */ -- cgit v1.2.3 From 473e162eea465e60578edb93341752e7f1c1dacc Mon Sep 17 00:00:00 2001 From: Jesper Nilsson Date: Mon, 24 Oct 2011 11:19:25 +0200 Subject: CRIS: Add _sdata to vmlinux.lds.S Fixes link error: LD vmlinux kernel/built-in.o: In function `core_kernel_data': (.text+0x13e44): undefined reference to `_sdata' Signed-off-by: Jesper Nilsson --- arch/cris/kernel/vmlinux.lds.S | 1 + 1 file changed, 1 insertion(+) (limited to 'arch') diff --git a/arch/cris/kernel/vmlinux.lds.S b/arch/cris/kernel/vmlinux.lds.S index a6990cb0f098..a68b983dcea1 100644 --- a/arch/cris/kernel/vmlinux.lds.S +++ b/arch/cris/kernel/vmlinux.lds.S @@ -52,6 +52,7 @@ SECTIONS EXCEPTION_TABLE(4) + _sdata = .; RODATA . = ALIGN (4); -- cgit v1.2.3 From dfb73a071f0259ba81ceea7b7f312f63dd18c73c Mon Sep 17 00:00:00 2001 From: Wanlong Gao Date: Wed, 3 Aug 2011 04:43:21 +0200 Subject: cris:fix the wrong function declear Fix wrong declear of write_stack_register. Signed-off-by: Wanlong Gao Signed-off-by: Jesper Nilsson --- arch/cris/arch-v10/kernel/kgdb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/cris/arch-v10/kernel/kgdb.c b/arch/cris/arch-v10/kernel/kgdb.c index b579dd02e098..37e6d2c50b76 100644 --- a/arch/cris/arch-v10/kernel/kgdb.c +++ b/arch/cris/arch-v10/kernel/kgdb.c @@ -264,7 +264,7 @@ static int write_register (int regno, char *val); /* Write a value to a specified register in the stack of a thread other than the current thread. */ -static write_stack_register (int thread_id, int regno, char *valptr); +static int write_stack_register(int thread_id, int regno, char *valptr); /* Read a value from a specified register in the register image. Returns the status of the read operation. The register value is returned in valptr. */ -- cgit v1.2.3 From 509a7c25729feab353502e1b544c614772a1d49a Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Tue, 3 Apr 2012 22:59:21 +0000 Subject: ixp4xx_eth: Support the get_ts_info ethtool method. Signed-off-by: Richard Cochran Signed-off-by: David S. Miller --- arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h | 3 +++ drivers/net/ethernet/xscale/ixp4xx_eth.c | 29 +++++++++++++++++++++++++++ drivers/ptp/ptp_ixp46x.c | 3 +++ 3 files changed, 35 insertions(+) (limited to 'arch') diff --git a/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h b/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h index 292d55ed2113..cf03614d250d 100644 --- a/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h +++ b/arch/arm/mach-ixp4xx/include/mach/ixp46x_ts.h @@ -75,4 +75,7 @@ struct ixp46x_ts_regs { #define TX_SNAPSHOT_LOCKED (1<<0) #define RX_SNAPSHOT_LOCKED (1<<1) +/* The ptp_ixp46x module will set this variable */ +extern int ixp46x_phc_index; + #endif diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c index 41a8b5a9849e..482648fcf0b6 100644 --- a/drivers/net/ethernet/xscale/ixp4xx_eth.c +++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c @@ -1002,12 +1002,41 @@ static int ixp4xx_nway_reset(struct net_device *dev) return phy_start_aneg(port->phydev); } +int ixp46x_phc_index = -1; + +static int ixp4xx_get_ts_info(struct net_device *dev, + struct ethtool_ts_info *info) +{ + if (!cpu_is_ixp46x()) { + info->so_timestamping = + SOF_TIMESTAMPING_TX_SOFTWARE | + SOF_TIMESTAMPING_RX_SOFTWARE | + SOF_TIMESTAMPING_SOFTWARE; + info->phc_index = -1; + return 0; + } + info->so_timestamping = + SOF_TIMESTAMPING_TX_HARDWARE | + SOF_TIMESTAMPING_RX_HARDWARE | + SOF_TIMESTAMPING_RAW_HARDWARE; + info->phc_index = ixp46x_phc_index; + info->tx_types = + (1 << HWTSTAMP_TX_OFF) | + (1 << HWTSTAMP_TX_ON); + info->rx_filters = + (1 << HWTSTAMP_FILTER_NONE) | + (1 << HWTSTAMP_FILTER_PTP_V1_L4_SYNC) | + (1 << HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ); + return 0; +} + static const struct ethtool_ops ixp4xx_ethtool_ops = { .get_drvinfo = ixp4xx_get_drvinfo, .get_settings = ixp4xx_get_settings, .set_settings = ixp4xx_set_settings, .nway_reset = ixp4xx_nway_reset, .get_link = ethtool_op_get_link, + .get_ts_info = ixp4xx_get_ts_info, }; diff --git a/drivers/ptp/ptp_ixp46x.c b/drivers/ptp/ptp_ixp46x.c index 6f2782bb5f41..9d13a71c5367 100644 --- a/drivers/ptp/ptp_ixp46x.c +++ b/drivers/ptp/ptp_ixp46x.c @@ -284,6 +284,7 @@ static void __exit ptp_ixp_exit(void) { free_irq(MASTER_IRQ, &ixp_clock); free_irq(SLAVE_IRQ, &ixp_clock); + ixp46x_phc_clock = -1; ptp_clock_unregister(ixp_clock.ptp_clock); } @@ -302,6 +303,8 @@ static int __init ptp_ixp_init(void) if (IS_ERR(ixp_clock.ptp_clock)) return PTR_ERR(ixp_clock.ptp_clock); + ixp46x_phc_clock = ptp_clock_index(ixp_clock.ptp_clock); + __raw_writel(DEFAULT_ADDEND, &ixp_clock.regs->addend); __raw_writel(1, &ixp_clock.regs->trgt_lo); __raw_writel(0, &ixp_clock.regs->trgt_hi); -- cgit v1.2.3 From 4d5914d628360c607dc426ccb1acaf23909ac546 Mon Sep 17 00:00:00 2001 From: Kautuk Consul Date: Tue, 20 Mar 2012 14:24:05 +0100 Subject: cris/mm/fault.c: Port OOM changes to do_page_fault Commit d065bd810b6deb67d4897a14bfe21f8eb526ba99 (mm: retry page fault when blocking on disk transfer) and commit 37b23e0525d393d48a7d59f870b3bc061a30ccdb (x86,mm: make pagefault killable) The above commits introduced changes into the x86 pagefault handler for making the page fault handler retryable as well as killable. These changes reduce the mmap_sem hold time, which is crucial during OOM killer invocation. Port these changes to CRIS. Signed-off-by: Kautuk Consul Signed-off-by: Jesper Nilsson --- arch/cris/mm/fault.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) (limited to 'arch') diff --git a/arch/cris/mm/fault.c b/arch/cris/mm/fault.c index b4760d86e1bb..45fd542cf173 100644 --- a/arch/cris/mm/fault.c +++ b/arch/cris/mm/fault.c @@ -58,6 +58,8 @@ do_page_fault(unsigned long address, struct pt_regs *regs, struct vm_area_struct * vma; siginfo_t info; int fault; + unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE | + ((writeaccess & 1) ? FAULT_FLAG_WRITE : 0); D(printk(KERN_DEBUG "Page fault for %lX on %X at %lX, prot %d write %d\n", @@ -115,6 +117,7 @@ do_page_fault(unsigned long address, struct pt_regs *regs, if (in_atomic() || !mm) goto no_context; +retry: down_read(&mm->mmap_sem); vma = find_vma(mm, address); if (!vma) @@ -163,7 +166,11 @@ do_page_fault(unsigned long address, struct pt_regs *regs, * the fault. */ - fault = handle_mm_fault(mm, vma, address, (writeaccess & 1) ? FAULT_FLAG_WRITE : 0); + fault = handle_mm_fault(mm, vma, address, flags); + + if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) + return; + if (unlikely(fault & VM_FAULT_ERROR)) { if (fault & VM_FAULT_OOM) goto out_of_memory; @@ -171,10 +178,24 @@ do_page_fault(unsigned long address, struct pt_regs *regs, goto do_sigbus; BUG(); } - if (fault & VM_FAULT_MAJOR) - tsk->maj_flt++; - else - tsk->min_flt++; + + if (flags & FAULT_FLAG_ALLOW_RETRY) { + if (fault & VM_FAULT_MAJOR) + tsk->maj_flt++; + else + tsk->min_flt++; + if (fault & VM_FAULT_RETRY) { + flags &= ~FAULT_FLAG_ALLOW_RETRY; + + /* + * No need to up_read(&mm->mmap_sem) as we would + * have already released it in __lock_page_or_retry + * in mm/filemap.c. + */ + + goto retry; + } + } up_read(&mm->mmap_sem); return; -- cgit v1.2.3 From e1f80f57443838f5f420c774744c50c81c178e2c Mon Sep 17 00:00:00 2001 From: KyongHo Cho Date: Wed, 4 Apr 2012 09:23:02 -0700 Subject: S5P: SYSMMU: Remove System MMU device driver This patch removes System MMU device driver from arm/plat-s5p tree for transition to implement IOMMU driver. Although controlling System MMU in this removing driver is similar to the new IOMMU driver in the following patch, the new one is almost rewrite of this driver and there is no benefit to moving the driver file from arch/arm/ to drivers/iommu. Signed-off-by: KyongHo Cho Signed-off-by: Kukjin Kim --- arch/arm/plat-s5p/Kconfig | 8 - arch/arm/plat-s5p/Makefile | 1 - arch/arm/plat-s5p/sysmmu.c | 313 ---------------------------- arch/arm/plat-samsung/include/plat/devs.h | 1 - arch/arm/plat-samsung/include/plat/sysmmu.h | 95 --------- 5 files changed, 418 deletions(-) delete mode 100644 arch/arm/plat-s5p/sysmmu.c delete mode 100644 arch/arm/plat-samsung/include/plat/sysmmu.h (limited to 'arch') diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig index 96bea3202304..2c1193c59928 100644 --- a/arch/arm/plat-s5p/Kconfig +++ b/arch/arm/plat-s5p/Kconfig @@ -50,14 +50,6 @@ config S5P_PM Common code for power management support on S5P and newer SoCs Note: Do not select this for S5P6440 and S5P6450. -comment "System MMU" - -config S5P_SYSTEM_MMU - bool "S5P SYSTEM MMU" - depends on ARCH_EXYNOS4 - help - Say Y here if you want to enable System MMU - config S5P_SLEEP bool help diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefile index 4bd824136659..4953d50707be 100644 --- a/arch/arm/plat-s5p/Makefile +++ b/arch/arm/plat-s5p/Makefile @@ -16,7 +16,6 @@ obj-y += clock.o obj-y += irq.o obj-$(CONFIG_S5P_EXT_INT) += irq-eint.o obj-$(CONFIG_S5P_GPIO_INT) += irq-gpioint.o -obj-$(CONFIG_S5P_SYSTEM_MMU) += sysmmu.o obj-$(CONFIG_S5P_PM) += pm.o irq-pm.o obj-$(CONFIG_S5P_SLEEP) += sleep.o obj-$(CONFIG_S5P_HRT) += s5p-time.o diff --git a/arch/arm/plat-s5p/sysmmu.c b/arch/arm/plat-s5p/sysmmu.c deleted file mode 100644 index c8bec9c7655d..000000000000 --- a/arch/arm/plat-s5p/sysmmu.c +++ /dev/null @@ -1,313 +0,0 @@ -/* linux/arch/arm/plat-s5p/sysmmu.c - * - * Copyright (c) 2010 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include - -#include - -#include -#include -#include - -#define CTRL_ENABLE 0x5 -#define CTRL_BLOCK 0x7 -#define CTRL_DISABLE 0x0 - -static struct device *dev; - -static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = { - S5P_PAGE_FAULT_ADDR, - S5P_AR_FAULT_ADDR, - S5P_AW_FAULT_ADDR, - S5P_DEFAULT_SLAVE_ADDR, - S5P_AR_FAULT_ADDR, - S5P_AR_FAULT_ADDR, - S5P_AW_FAULT_ADDR, - S5P_AW_FAULT_ADDR -}; - -static char *sysmmu_fault_name[SYSMMU_FAULTS_NUM] = { - "PAGE FAULT", - "AR MULTI-HIT FAULT", - "AW MULTI-HIT FAULT", - "BUS ERROR", - "AR SECURITY PROTECTION FAULT", - "AR ACCESS PROTECTION FAULT", - "AW SECURITY PROTECTION FAULT", - "AW ACCESS PROTECTION FAULT" -}; - -static int (*fault_handlers[S5P_SYSMMU_TOTAL_IPNUM])( - enum S5P_SYSMMU_INTERRUPT_TYPE itype, - unsigned long pgtable_base, - unsigned long fault_addr); - -/* - * If adjacent 2 bits are true, the system MMU is enabled. - * The system MMU is disabled, otherwise. - */ -static unsigned long sysmmu_states; - -static inline void set_sysmmu_active(sysmmu_ips ips) -{ - sysmmu_states |= 3 << (ips * 2); -} - -static inline void set_sysmmu_inactive(sysmmu_ips ips) -{ - sysmmu_states &= ~(3 << (ips * 2)); -} - -static inline int is_sysmmu_active(sysmmu_ips ips) -{ - return sysmmu_states & (3 << (ips * 2)); -} - -static void __iomem *sysmmusfrs[S5P_SYSMMU_TOTAL_IPNUM]; - -static inline void sysmmu_block(sysmmu_ips ips) -{ - __raw_writel(CTRL_BLOCK, sysmmusfrs[ips] + S5P_MMU_CTRL); - dev_dbg(dev, "%s is blocked.\n", sysmmu_ips_name[ips]); -} - -static inline void sysmmu_unblock(sysmmu_ips ips) -{ - __raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL); - dev_dbg(dev, "%s is unblocked.\n", sysmmu_ips_name[ips]); -} - -static inline void __sysmmu_tlb_invalidate(sysmmu_ips ips) -{ - __raw_writel(0x1, sysmmusfrs[ips] + S5P_MMU_FLUSH); - dev_dbg(dev, "TLB of %s is invalidated.\n", sysmmu_ips_name[ips]); -} - -static inline void __sysmmu_set_ptbase(sysmmu_ips ips, unsigned long pgd) -{ - if (unlikely(pgd == 0)) { - pgd = (unsigned long)ZERO_PAGE(0); - __raw_writel(0x20, sysmmusfrs[ips] + S5P_MMU_CFG); /* 4KB LV1 */ - } else { - __raw_writel(0x0, sysmmusfrs[ips] + S5P_MMU_CFG); /* 16KB LV1 */ - } - - __raw_writel(pgd, sysmmusfrs[ips] + S5P_PT_BASE_ADDR); - - dev_dbg(dev, "Page table base of %s is initialized with 0x%08lX.\n", - sysmmu_ips_name[ips], pgd); - __sysmmu_tlb_invalidate(ips); -} - -void sysmmu_set_fault_handler(sysmmu_ips ips, - int (*handler)(enum S5P_SYSMMU_INTERRUPT_TYPE itype, - unsigned long pgtable_base, - unsigned long fault_addr)) -{ - BUG_ON(!((ips >= SYSMMU_MDMA) && (ips < S5P_SYSMMU_TOTAL_IPNUM))); - fault_handlers[ips] = handler; -} - -static irqreturn_t s5p_sysmmu_irq(int irq, void *dev_id) -{ - /* SYSMMU is in blocked when interrupt occurred. */ - unsigned long base = 0; - sysmmu_ips ips = (sysmmu_ips)dev_id; - enum S5P_SYSMMU_INTERRUPT_TYPE itype; - - itype = (enum S5P_SYSMMU_INTERRUPT_TYPE) - __ffs(__raw_readl(sysmmusfrs[ips] + S5P_INT_STATUS)); - - BUG_ON(!((itype >= 0) && (itype < 8))); - - dev_alert(dev, "%s occurred by %s.\n", sysmmu_fault_name[itype], - sysmmu_ips_name[ips]); - - if (fault_handlers[ips]) { - unsigned long addr; - - base = __raw_readl(sysmmusfrs[ips] + S5P_PT_BASE_ADDR); - addr = __raw_readl(sysmmusfrs[ips] + fault_reg_offset[itype]); - - if (fault_handlers[ips](itype, base, addr)) { - __raw_writel(1 << itype, - sysmmusfrs[ips] + S5P_INT_CLEAR); - dev_notice(dev, "%s from %s is resolved." - " Retrying translation.\n", - sysmmu_fault_name[itype], sysmmu_ips_name[ips]); - } else { - base = 0; - } - } - - sysmmu_unblock(ips); - - if (!base) - dev_notice(dev, "%s from %s is not handled.\n", - sysmmu_fault_name[itype], sysmmu_ips_name[ips]); - - return IRQ_HANDLED; -} - -void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd) -{ - if (is_sysmmu_active(ips)) { - sysmmu_block(ips); - __sysmmu_set_ptbase(ips, pgd); - sysmmu_unblock(ips); - } else { - dev_dbg(dev, "%s is disabled. " - "Skipping initializing page table base.\n", - sysmmu_ips_name[ips]); - } -} - -void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd) -{ - if (!is_sysmmu_active(ips)) { - sysmmu_clk_enable(ips); - - __sysmmu_set_ptbase(ips, pgd); - - __raw_writel(CTRL_ENABLE, sysmmusfrs[ips] + S5P_MMU_CTRL); - - set_sysmmu_active(ips); - dev_dbg(dev, "%s is enabled.\n", sysmmu_ips_name[ips]); - } else { - dev_dbg(dev, "%s is already enabled.\n", sysmmu_ips_name[ips]); - } -} - -void s5p_sysmmu_disable(sysmmu_ips ips) -{ - if (is_sysmmu_active(ips)) { - __raw_writel(CTRL_DISABLE, sysmmusfrs[ips] + S5P_MMU_CTRL); - set_sysmmu_inactive(ips); - sysmmu_clk_disable(ips); - dev_dbg(dev, "%s is disabled.\n", sysmmu_ips_name[ips]); - } else { - dev_dbg(dev, "%s is already disabled.\n", sysmmu_ips_name[ips]); - } -} - -void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips) -{ - if (is_sysmmu_active(ips)) { - sysmmu_block(ips); - __sysmmu_tlb_invalidate(ips); - sysmmu_unblock(ips); - } else { - dev_dbg(dev, "%s is disabled. " - "Skipping invalidating TLB.\n", sysmmu_ips_name[ips]); - } -} - -static int s5p_sysmmu_probe(struct platform_device *pdev) -{ - int i, ret; - struct resource *res, *mem; - - dev = &pdev->dev; - - for (i = 0; i < S5P_SYSMMU_TOTAL_IPNUM; i++) { - int irq; - - sysmmu_clk_init(dev, i); - sysmmu_clk_disable(i); - - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - if (!res) { - dev_err(dev, "Failed to get the resource of %s.\n", - sysmmu_ips_name[i]); - ret = -ENODEV; - goto err_res; - } - - mem = request_mem_region(res->start, resource_size(res), - pdev->name); - if (!mem) { - dev_err(dev, "Failed to request the memory region of %s.\n", - sysmmu_ips_name[i]); - ret = -EBUSY; - goto err_res; - } - - sysmmusfrs[i] = ioremap(res->start, resource_size(res)); - if (!sysmmusfrs[i]) { - dev_err(dev, "Failed to ioremap() for %s.\n", - sysmmu_ips_name[i]); - ret = -ENXIO; - goto err_reg; - } - - irq = platform_get_irq(pdev, i); - if (irq <= 0) { - dev_err(dev, "Failed to get the IRQ resource of %s.\n", - sysmmu_ips_name[i]); - ret = -ENOENT; - goto err_map; - } - - if (request_irq(irq, s5p_sysmmu_irq, IRQF_DISABLED, - pdev->name, (void *)i)) { - dev_err(dev, "Failed to request IRQ for %s.\n", - sysmmu_ips_name[i]); - ret = -ENOENT; - goto err_map; - } - } - - return 0; - -err_map: - iounmap(sysmmusfrs[i]); -err_reg: - release_mem_region(mem->start, resource_size(mem)); -err_res: - return ret; -} - -static int s5p_sysmmu_remove(struct platform_device *pdev) -{ - return 0; -} -int s5p_sysmmu_runtime_suspend(struct device *dev) -{ - return 0; -} - -int s5p_sysmmu_runtime_resume(struct device *dev) -{ - return 0; -} - -const struct dev_pm_ops s5p_sysmmu_pm_ops = { - .runtime_suspend = s5p_sysmmu_runtime_suspend, - .runtime_resume = s5p_sysmmu_runtime_resume, -}; - -static struct platform_driver s5p_sysmmu_driver = { - .probe = s5p_sysmmu_probe, - .remove = s5p_sysmmu_remove, - .driver = { - .owner = THIS_MODULE, - .name = "s5p-sysmmu", - .pm = &s5p_sysmmu_pm_ops, - } -}; - -static int __init s5p_sysmmu_init(void) -{ - return platform_driver_register(&s5p_sysmmu_driver); -} -arch_initcall(s5p_sysmmu_init); diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h index 2155d4af62a3..4067d1dd7f1c 100644 --- a/arch/arm/plat-samsung/include/plat/devs.h +++ b/arch/arm/plat-samsung/include/plat/devs.h @@ -133,7 +133,6 @@ extern struct platform_device exynos4_device_pcm1; extern struct platform_device exynos4_device_pcm2; extern struct platform_device exynos4_device_pd[]; extern struct platform_device exynos4_device_spdif; -extern struct platform_device exynos4_device_sysmmu; extern struct platform_device samsung_asoc_dma; extern struct platform_device samsung_asoc_idma; diff --git a/arch/arm/plat-samsung/include/plat/sysmmu.h b/arch/arm/plat-samsung/include/plat/sysmmu.h deleted file mode 100644 index 5fe8ee01a5ba..000000000000 --- a/arch/arm/plat-samsung/include/plat/sysmmu.h +++ /dev/null @@ -1,95 +0,0 @@ -/* linux/arch/arm/plat-samsung/include/plat/sysmmu.h - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * Samsung System MMU driver for S5P platform - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef __PLAT_SAMSUNG_SYSMMU_H -#define __PLAT_SAMSUNG_SYSMMU_H __FILE__ - -enum S5P_SYSMMU_INTERRUPT_TYPE { - SYSMMU_PAGEFAULT, - SYSMMU_AR_MULTIHIT, - SYSMMU_AW_MULTIHIT, - SYSMMU_BUSERROR, - SYSMMU_AR_SECURITY, - SYSMMU_AR_ACCESS, - SYSMMU_AW_SECURITY, - SYSMMU_AW_PROTECTION, /* 7 */ - SYSMMU_FAULTS_NUM -}; - -#ifdef CONFIG_S5P_SYSTEM_MMU - -#include - -/** - * s5p_sysmmu_enable() - enable system mmu of ip - * @ips: The ip connected system mmu. - * #pgd: Base physical address of the 1st level page table - * - * This function enable system mmu to transfer address - * from virtual address to physical address - */ -void s5p_sysmmu_enable(sysmmu_ips ips, unsigned long pgd); - -/** - * s5p_sysmmu_disable() - disable sysmmu mmu of ip - * @ips: The ip connected system mmu. - * - * This function disable system mmu to transfer address - * from virtual address to physical address - */ -void s5p_sysmmu_disable(sysmmu_ips ips); - -/** - * s5p_sysmmu_set_tablebase_pgd() - set page table base address to refer page table - * @ips: The ip connected system mmu. - * @pgd: The page table base address. - * - * This function set page table base address - * When system mmu transfer address from virtaul address to physical address, - * system mmu refer address information from page table - */ -void s5p_sysmmu_set_tablebase_pgd(sysmmu_ips ips, unsigned long pgd); - -/** - * s5p_sysmmu_tlb_invalidate() - flush all TLB entry in system mmu - * @ips: The ip connected system mmu. - * - * This function flush all TLB entry in system mmu - */ -void s5p_sysmmu_tlb_invalidate(sysmmu_ips ips); - -/** s5p_sysmmu_set_fault_handler() - Fault handler for System MMUs - * @itype: type of fault. - * @pgtable_base: the physical address of page table base. This is 0 if @ips is - * SYSMMU_BUSERROR. - * @fault_addr: the device (virtual) address that the System MMU tried to - * translated. This is 0 if @ips is SYSMMU_BUSERROR. - * Called when interrupt occurred by the System MMUs - * The device drivers of peripheral devices that has a System MMU can implement - * a fault handler to resolve address translation fault by System MMU. - * The meanings of return value and parameters are described below. - - * return value: non-zero if the fault is correctly resolved. - * zero if the fault is not handled. - */ -void s5p_sysmmu_set_fault_handler(sysmmu_ips ips, - int (*handler)(enum S5P_SYSMMU_INTERRUPT_TYPE itype, - unsigned long pgtable_base, - unsigned long fault_addr)); -#else -#define s5p_sysmmu_enable(ips, pgd) do { } while (0) -#define s5p_sysmmu_disable(ips) do { } while (0) -#define s5p_sysmmu_set_tablebase_pgd(ips, pgd) do { } while (0) -#define s5p_sysmmu_tlb_invalidate(ips) do { } while (0) -#define s5p_sysmmu_set_fault_handler(ips, handler) do { } while (0) -#endif -#endif /* __ASM_PLAT_SYSMMU_H */ -- cgit v1.2.3 From bca10b906f8d2e4f177bff047b9d623941e454f7 Mon Sep 17 00:00:00 2001 From: KyongHo Cho Date: Wed, 4 Apr 2012 09:23:02 -0700 Subject: ARM: EXYNOS: Change System MMU platform device definitions Handling System MMUs with an identifier is not flexible to manage System MMU platform devices because of the following reasons: 1. A device driver which needs to handle System MMU must know the ID. 2. A System MMU may not present in some implementations of Exynos family. 3. Handling System MMU with IOMMU API does not require an ID. This patch is the result of removing ID of System MMUs. Instead, a device driver that needs to handle its System MMU must use IOMMU API while its descriptor of platform device is given. This patch also includes the following enhancements: - A System MMU device becomes a child if its power domain device. - clkdev Signed-off-by: KyongHo Cho Signed-off-by: Kukjin Kim --- arch/arm/mach-exynos/Kconfig | 10 +- arch/arm/mach-exynos/Makefile | 2 +- arch/arm/mach-exynos/clock-exynos4.c | 79 ++-- arch/arm/mach-exynos/clock-exynos4.h | 2 + arch/arm/mach-exynos/clock-exynos4210.c | 11 + arch/arm/mach-exynos/clock-exynos4212.c | 28 +- arch/arm/mach-exynos/clock-exynos5.c | 90 +++++ arch/arm/mach-exynos/dev-sysmmu.c | 457 +++++++++++++----------- arch/arm/mach-exynos/include/mach/irqs.h | 25 +- arch/arm/mach-exynos/include/mach/map.h | 38 ++ arch/arm/mach-exynos/include/mach/regs-clock.h | 5 + arch/arm/mach-exynos/include/mach/regs-sysmmu.h | 28 -- arch/arm/mach-exynos/include/mach/sysmmu.h | 88 +++-- arch/arm/mach-exynos/mach-armlex4210.c | 1 - arch/arm/mach-exynos/mach-smdkv310.c | 1 - 15 files changed, 529 insertions(+), 336 deletions(-) delete mode 100644 arch/arm/mach-exynos/include/mach/regs-sysmmu.h (limited to 'arch') diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 0491ceef1cda..801c738d8f0e 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -85,10 +85,10 @@ config EXYNOS4_SETUP_FIMD0 help Common setup code for FIMD0. -config EXYNOS4_DEV_SYSMMU +config EXYNOS_DEV_SYSMMU bool help - Common setup code for SYSTEM MMU in EXYNOS4 + Common setup code for SYSTEM MMU in EXYNOS platforms config EXYNOS4_DEV_DWMCI bool @@ -200,12 +200,12 @@ config MACH_SMDKV310 select S3C_DEV_HSMMC2 select S3C_DEV_HSMMC3 select SAMSUNG_DEV_BACKLIGHT + select EXYNOS_DEV_SYSMMU select EXYNOS4_DEV_AHCI select SAMSUNG_DEV_KEYPAD select EXYNOS4_DEV_DMA select SAMSUNG_DEV_PWM select EXYNOS4_DEV_USB_OHCI - select EXYNOS4_DEV_SYSMMU select EXYNOS4_SETUP_FIMD0 select EXYNOS4_SETUP_I2C1 select EXYNOS4_SETUP_KEYPAD @@ -224,7 +224,6 @@ config MACH_ARMLEX4210 select S3C_DEV_HSMMC3 select EXYNOS4_DEV_AHCI select EXYNOS4_DEV_DMA - select EXYNOS4_DEV_SYSMMU select EXYNOS4_SETUP_SDHCI help Machine support for Samsung ARMLEX4210 based on EXYNOS4210 @@ -251,6 +250,7 @@ config MACH_UNIVERSAL_C210 select S5P_DEV_MFC select S5P_DEV_ONENAND select S5P_DEV_TV + select EXYNOS_DEV_SYSMMU select EXYNOS4_DEV_DMA select EXYNOS4_SETUP_FIMD0 select EXYNOS4_SETUP_I2C1 @@ -322,6 +322,7 @@ config MACH_ORIGEN select S5P_DEV_USB_EHCI select SAMSUNG_DEV_BACKLIGHT select SAMSUNG_DEV_PWM + select EXYNOS_DEV_SYSMMU select EXYNOS4_DEV_DMA select EXYNOS4_DEV_USB_OHCI select EXYNOS4_SETUP_FIMD0 @@ -345,6 +346,7 @@ config MACH_SMDK4212 select SAMSUNG_DEV_BACKLIGHT select SAMSUNG_DEV_KEYPAD select SAMSUNG_DEV_PWM + select EXYNOS_DEV_SYSMMU select EXYNOS4_DEV_DMA select EXYNOS4_SETUP_I2C1 select EXYNOS4_SETUP_I2C3 diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index 8631840d1b5e..272625231c73 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -50,7 +50,7 @@ obj-$(CONFIG_MACH_EXYNOS5_DT) += mach-exynos5-dt.o obj-y += dev-uart.o obj-$(CONFIG_ARCH_EXYNOS4) += dev-audio.o obj-$(CONFIG_EXYNOS4_DEV_AHCI) += dev-ahci.o -obj-$(CONFIG_EXYNOS4_DEV_SYSMMU) += dev-sysmmu.o +obj-$(CONFIG_EXYNOS_DEV_SYSMMU) += dev-sysmmu.o obj-$(CONFIG_EXYNOS4_DEV_DWMCI) += dev-dwmci.o obj-$(CONFIG_EXYNOS4_DEV_DMA) += dma.o obj-$(CONFIG_EXYNOS4_DEV_USB_OHCI) += dev-ohci.o diff --git a/arch/arm/mach-exynos/clock-exynos4.c b/arch/arm/mach-exynos/clock-exynos4.c index df54c2a92225..428731197471 100644 --- a/arch/arm/mach-exynos/clock-exynos4.c +++ b/arch/arm/mach-exynos/clock-exynos4.c @@ -168,7 +168,7 @@ static int exynos4_clk_ip_tv_ctrl(struct clk *clk, int enable) return s5p_gatectrl(EXYNOS4_CLKGATE_IP_TV, clk, enable); } -static int exynos4_clk_ip_image_ctrl(struct clk *clk, int enable) +int exynos4_clk_ip_image_ctrl(struct clk *clk, int enable) { return s5p_gatectrl(EXYNOS4_CLKGATE_IP_IMAGE, clk, enable); } @@ -198,6 +198,11 @@ static int exynos4_clk_ip_perir_ctrl(struct clk *clk, int enable) return s5p_gatectrl(EXYNOS4_CLKGATE_IP_PERIR, clk, enable); } +int exynos4_clk_ip_dmc_ctrl(struct clk *clk, int enable) +{ + return s5p_gatectrl(EXYNOS4_CLKGATE_IP_DMC, clk, enable); +} + static int exynos4_clk_hdmiphy_ctrl(struct clk *clk, int enable) { return s5p_gatectrl(S5P_HDMI_PHY_CONTROL, clk, enable); @@ -678,61 +683,55 @@ static struct clk exynos4_init_clocks_off[] = { .enable = exynos4_clk_ip_peril_ctrl, .ctrlbit = (1 << 14), }, { - .name = "SYSMMU_MDMA", + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(mfc_l, 0), + .enable = exynos4_clk_ip_mfc_ctrl, + .ctrlbit = (1 << 1), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(mfc_r, 1), + .enable = exynos4_clk_ip_mfc_ctrl, + .ctrlbit = (1 << 2), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(tv, 2), + .enable = exynos4_clk_ip_tv_ctrl, + .ctrlbit = (1 << 4), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(jpeg, 3), + .enable = exynos4_clk_ip_cam_ctrl, + .ctrlbit = (1 << 11), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(rot, 4), .enable = exynos4_clk_ip_image_ctrl, - .ctrlbit = (1 << 5), + .ctrlbit = (1 << 4), }, { - .name = "SYSMMU_FIMC0", + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(fimc0, 5), .enable = exynos4_clk_ip_cam_ctrl, .ctrlbit = (1 << 7), }, { - .name = "SYSMMU_FIMC1", + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(fimc1, 6), .enable = exynos4_clk_ip_cam_ctrl, .ctrlbit = (1 << 8), }, { - .name = "SYSMMU_FIMC2", + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(fimc2, 7), .enable = exynos4_clk_ip_cam_ctrl, .ctrlbit = (1 << 9), }, { - .name = "SYSMMU_FIMC3", + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(fimc3, 8), .enable = exynos4_clk_ip_cam_ctrl, .ctrlbit = (1 << 10), }, { - .name = "SYSMMU_JPEG", - .enable = exynos4_clk_ip_cam_ctrl, - .ctrlbit = (1 << 11), - }, { - .name = "SYSMMU_FIMD0", + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(fimd0, 10), .enable = exynos4_clk_ip_lcd0_ctrl, .ctrlbit = (1 << 4), - }, { - .name = "SYSMMU_FIMD1", - .enable = exynos4_clk_ip_lcd1_ctrl, - .ctrlbit = (1 << 4), - }, { - .name = "SYSMMU_PCIe", - .enable = exynos4_clk_ip_fsys_ctrl, - .ctrlbit = (1 << 18), - }, { - .name = "SYSMMU_G2D", - .enable = exynos4_clk_ip_image_ctrl, - .ctrlbit = (1 << 3), - }, { - .name = "SYSMMU_ROTATOR", - .enable = exynos4_clk_ip_image_ctrl, - .ctrlbit = (1 << 4), - }, { - .name = "SYSMMU_TV", - .enable = exynos4_clk_ip_tv_ctrl, - .ctrlbit = (1 << 4), - }, { - .name = "SYSMMU_MFC_L", - .enable = exynos4_clk_ip_mfc_ctrl, - .ctrlbit = (1 << 1), - }, { - .name = "SYSMMU_MFC_R", - .enable = exynos4_clk_ip_mfc_ctrl, - .ctrlbit = (1 << 2), } }; diff --git a/arch/arm/mach-exynos/clock-exynos4.h b/arch/arm/mach-exynos/clock-exynos4.h index cb71c29c14d1..28a119701182 100644 --- a/arch/arm/mach-exynos/clock-exynos4.h +++ b/arch/arm/mach-exynos/clock-exynos4.h @@ -26,5 +26,7 @@ extern struct clk *exynos4_clkset_group_list[]; extern int exynos4_clksrc_mask_fsys_ctrl(struct clk *clk, int enable); extern int exynos4_clk_ip_fsys_ctrl(struct clk *clk, int enable); extern int exynos4_clk_ip_lcd1_ctrl(struct clk *clk, int enable); +extern int exynos4_clk_ip_image_ctrl(struct clk *clk, int enable); +extern int exynos4_clk_ip_dmc_ctrl(struct clk *clk, int enable); #endif /* __ASM_ARCH_CLOCK_H */ diff --git a/arch/arm/mach-exynos/clock-exynos4210.c b/arch/arm/mach-exynos/clock-exynos4210.c index 3b131e4b6ef5..b8689ff60baf 100644 --- a/arch/arm/mach-exynos/clock-exynos4210.c +++ b/arch/arm/mach-exynos/clock-exynos4210.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "common.h" #include "clock-exynos4.h" @@ -94,6 +95,16 @@ static struct clk init_clocks_off[] = { .devname = "exynos4-fb.1", .enable = exynos4_clk_ip_lcd1_ctrl, .ctrlbit = (1 << 0), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(2d, 14), + .enable = exynos4_clk_ip_image_ctrl, + .ctrlbit = (1 << 3), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(fimd1, 11), + .enable = exynos4_clk_ip_lcd1_ctrl, + .ctrlbit = (1 << 4), }, }; diff --git a/arch/arm/mach-exynos/clock-exynos4212.c b/arch/arm/mach-exynos/clock-exynos4212.c index 3ecc01e06f74..98823120570e 100644 --- a/arch/arm/mach-exynos/clock-exynos4212.c +++ b/arch/arm/mach-exynos/clock-exynos4212.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "common.h" #include "clock-exynos4.h" @@ -39,6 +40,16 @@ static struct sleep_save exynos4212_clock_save[] = { }; #endif +static int exynos4212_clk_ip_isp0_ctrl(struct clk *clk, int enable) +{ + return s5p_gatectrl(EXYNOS4_CLKGATE_IP_ISP0, clk, enable); +} + +static int exynos4212_clk_ip_isp1_ctrl(struct clk *clk, int enable) +{ + return s5p_gatectrl(EXYNOS4_CLKGATE_IP_ISP1, clk, enable); +} + static struct clk *clk_src_mpll_user_list[] = { [0] = &clk_fin_mpll, [1] = &exynos4_clk_mout_mpll.clk, @@ -66,7 +77,22 @@ static struct clksrc_clk clksrcs[] = { }; static struct clk init_clocks_off[] = { - /* nothing here yet */ + { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(2d, 14), + .enable = exynos4_clk_ip_dmc_ctrl, + .ctrlbit = (1 << 24), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(isp, 9), + .enable = exynos4212_clk_ip_isp0_ctrl, + .ctrlbit = (7 << 8), + }, { + .name = SYSMMU_CLOCK_NAME2, + .devname = SYSMMU_CLOCK_DEVNAME(isp, 9), + .enable = exynos4212_clk_ip_isp1_ctrl, + .ctrlbit = (1 << 4), + } }; #ifdef CONFIG_PM_SLEEP diff --git a/arch/arm/mach-exynos/clock-exynos5.c b/arch/arm/mach-exynos/clock-exynos5.c index d013982d0f8e..3320ad140ebe 100644 --- a/arch/arm/mach-exynos/clock-exynos5.c +++ b/arch/arm/mach-exynos/clock-exynos5.c @@ -82,6 +82,11 @@ static int exynos5_clksrc_mask_peric0_ctrl(struct clk *clk, int enable) return s5p_gatectrl(EXYNOS5_CLKSRC_MASK_PERIC0, clk, enable); } +static int exynos5_clk_ip_acp_ctrl(struct clk *clk, int enable) +{ + return s5p_gatectrl(EXYNOS5_CLKGATE_IP_ACP, clk, enable); +} + static int exynos5_clk_ip_core_ctrl(struct clk *clk, int enable) { return s5p_gatectrl(EXYNOS5_CLKGATE_IP_CORE, clk, enable); @@ -127,6 +132,21 @@ static int exynos5_clk_ip_peris_ctrl(struct clk *clk, int enable) return s5p_gatectrl(EXYNOS5_CLKGATE_IP_PERIS, clk, enable); } +static int exynos5_clk_ip_gscl_ctrl(struct clk *clk, int enable) +{ + return s5p_gatectrl(EXYNOS5_CLKGATE_IP_GSCL, clk, enable); +} + +static int exynos5_clk_ip_isp0_ctrl(struct clk *clk, int enable) +{ + return s5p_gatectrl(EXYNOS5_CLKGATE_IP_ISP0, clk, enable); +} + +static int exynos5_clk_ip_isp1_ctrl(struct clk *clk, int enable) +{ + return s5p_gatectrl(EXYNOS5_CLKGATE_IP_ISP1, clk, enable); +} + /* Core list of CMU_CPU side */ static struct clksrc_clk exynos5_clk_mout_apll = { @@ -630,6 +650,76 @@ static struct clk exynos5_init_clocks_off[] = { .parent = &exynos5_clk_aclk_66.clk, .enable = exynos5_clk_ip_peric_ctrl, .ctrlbit = (1 << 14), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(mfc_l, 0), + .enable = &exynos5_clk_ip_mfc_ctrl, + .ctrlbit = (1 << 1), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(mfc_r, 1), + .enable = &exynos5_clk_ip_mfc_ctrl, + .ctrlbit = (1 << 2), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(tv, 2), + .enable = &exynos5_clk_ip_disp1_ctrl, + .ctrlbit = (1 << 9) + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(jpeg, 3), + .enable = &exynos5_clk_ip_gen_ctrl, + .ctrlbit = (1 << 7), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(rot, 4), + .enable = &exynos5_clk_ip_gen_ctrl, + .ctrlbit = (1 << 6) + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(gsc0, 5), + .enable = &exynos5_clk_ip_gscl_ctrl, + .ctrlbit = (1 << 7), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(gsc1, 6), + .enable = &exynos5_clk_ip_gscl_ctrl, + .ctrlbit = (1 << 8), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(gsc2, 7), + .enable = &exynos5_clk_ip_gscl_ctrl, + .ctrlbit = (1 << 9), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(gsc3, 8), + .enable = &exynos5_clk_ip_gscl_ctrl, + .ctrlbit = (1 << 10), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(isp, 9), + .enable = &exynos5_clk_ip_isp0_ctrl, + .ctrlbit = (0x3F << 8), + }, { + .name = SYSMMU_CLOCK_NAME2, + .devname = SYSMMU_CLOCK_DEVNAME(isp, 9), + .enable = &exynos5_clk_ip_isp1_ctrl, + .ctrlbit = (0xF << 4), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(camif0, 12), + .enable = &exynos5_clk_ip_gscl_ctrl, + .ctrlbit = (1 << 11), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(camif1, 13), + .enable = &exynos5_clk_ip_gscl_ctrl, + .ctrlbit = (1 << 12), + }, { + .name = SYSMMU_CLOCK_NAME, + .devname = SYSMMU_CLOCK_DEVNAME(2d, 14), + .enable = &exynos5_clk_ip_acp_ctrl, + .ctrlbit = (1 << 7) } }; diff --git a/arch/arm/mach-exynos/dev-sysmmu.c b/arch/arm/mach-exynos/dev-sysmmu.c index 781563fcb156..c5b1ea301df0 100644 --- a/arch/arm/mach-exynos/dev-sysmmu.c +++ b/arch/arm/mach-exynos/dev-sysmmu.c @@ -1,9 +1,9 @@ -/* linux/arch/arm/mach-exynos4/dev-sysmmu.c +/* linux/arch/arm/mach-exynos/dev-sysmmu.c * - * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * Copyright (c) 2010-2012 Samsung Electronics Co., Ltd. * http://www.samsung.com * - * EXYNOS4 - System MMU support + * EXYNOS - System MMU support * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,222 +12,263 @@ #include #include -#include + +#include #include #include #include -#include - -/* These names must be equal to the clock names in mach-exynos4/clock.c */ -const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM] = { - "SYSMMU_MDMA" , - "SYSMMU_SSS" , - "SYSMMU_FIMC0" , - "SYSMMU_FIMC1" , - "SYSMMU_FIMC2" , - "SYSMMU_FIMC3" , - "SYSMMU_JPEG" , - "SYSMMU_FIMD0" , - "SYSMMU_FIMD1" , - "SYSMMU_PCIe" , - "SYSMMU_G2D" , - "SYSMMU_ROTATOR", - "SYSMMU_MDMA2" , - "SYSMMU_TV" , - "SYSMMU_MFC_L" , - "SYSMMU_MFC_R" , -}; -static struct resource exynos4_sysmmu_resource[] = { - [0] = { - .start = EXYNOS4_PA_SYSMMU_MDMA, - .end = EXYNOS4_PA_SYSMMU_MDMA + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_SYSMMU_MDMA0_0, - .end = IRQ_SYSMMU_MDMA0_0, - .flags = IORESOURCE_IRQ, - }, - [2] = { - .start = EXYNOS4_PA_SYSMMU_SSS, - .end = EXYNOS4_PA_SYSMMU_SSS + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [3] = { - .start = IRQ_SYSMMU_SSS_0, - .end = IRQ_SYSMMU_SSS_0, - .flags = IORESOURCE_IRQ, - }, - [4] = { - .start = EXYNOS4_PA_SYSMMU_FIMC0, - .end = EXYNOS4_PA_SYSMMU_FIMC0 + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [5] = { - .start = IRQ_SYSMMU_FIMC0_0, - .end = IRQ_SYSMMU_FIMC0_0, - .flags = IORESOURCE_IRQ, - }, - [6] = { - .start = EXYNOS4_PA_SYSMMU_FIMC1, - .end = EXYNOS4_PA_SYSMMU_FIMC1 + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [7] = { - .start = IRQ_SYSMMU_FIMC1_0, - .end = IRQ_SYSMMU_FIMC1_0, - .flags = IORESOURCE_IRQ, - }, - [8] = { - .start = EXYNOS4_PA_SYSMMU_FIMC2, - .end = EXYNOS4_PA_SYSMMU_FIMC2 + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [9] = { - .start = IRQ_SYSMMU_FIMC2_0, - .end = IRQ_SYSMMU_FIMC2_0, - .flags = IORESOURCE_IRQ, - }, - [10] = { - .start = EXYNOS4_PA_SYSMMU_FIMC3, - .end = EXYNOS4_PA_SYSMMU_FIMC3 + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [11] = { - .start = IRQ_SYSMMU_FIMC3_0, - .end = IRQ_SYSMMU_FIMC3_0, - .flags = IORESOURCE_IRQ, - }, - [12] = { - .start = EXYNOS4_PA_SYSMMU_JPEG, - .end = EXYNOS4_PA_SYSMMU_JPEG + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [13] = { - .start = IRQ_SYSMMU_JPEG_0, - .end = IRQ_SYSMMU_JPEG_0, - .flags = IORESOURCE_IRQ, - }, - [14] = { - .start = EXYNOS4_PA_SYSMMU_FIMD0, - .end = EXYNOS4_PA_SYSMMU_FIMD0 + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [15] = { - .start = IRQ_SYSMMU_LCD0_M0_0, - .end = IRQ_SYSMMU_LCD0_M0_0, - .flags = IORESOURCE_IRQ, - }, - [16] = { - .start = EXYNOS4_PA_SYSMMU_FIMD1, - .end = EXYNOS4_PA_SYSMMU_FIMD1 + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [17] = { - .start = IRQ_SYSMMU_LCD1_M1_0, - .end = IRQ_SYSMMU_LCD1_M1_0, - .flags = IORESOURCE_IRQ, - }, - [18] = { - .start = EXYNOS4_PA_SYSMMU_PCIe, - .end = EXYNOS4_PA_SYSMMU_PCIe + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [19] = { - .start = IRQ_SYSMMU_PCIE_0, - .end = IRQ_SYSMMU_PCIE_0, - .flags = IORESOURCE_IRQ, - }, - [20] = { - .start = EXYNOS4_PA_SYSMMU_G2D, - .end = EXYNOS4_PA_SYSMMU_G2D + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [21] = { - .start = IRQ_SYSMMU_2D_0, - .end = IRQ_SYSMMU_2D_0, - .flags = IORESOURCE_IRQ, - }, - [22] = { - .start = EXYNOS4_PA_SYSMMU_ROTATOR, - .end = EXYNOS4_PA_SYSMMU_ROTATOR + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [23] = { - .start = IRQ_SYSMMU_ROTATOR_0, - .end = IRQ_SYSMMU_ROTATOR_0, - .flags = IORESOURCE_IRQ, - }, - [24] = { - .start = EXYNOS4_PA_SYSMMU_MDMA2, - .end = EXYNOS4_PA_SYSMMU_MDMA2 + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [25] = { - .start = IRQ_SYSMMU_MDMA1_0, - .end = IRQ_SYSMMU_MDMA1_0, - .flags = IORESOURCE_IRQ, - }, - [26] = { - .start = EXYNOS4_PA_SYSMMU_TV, - .end = EXYNOS4_PA_SYSMMU_TV + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [27] = { - .start = IRQ_SYSMMU_TV_M0_0, - .end = IRQ_SYSMMU_TV_M0_0, - .flags = IORESOURCE_IRQ, - }, - [28] = { - .start = EXYNOS4_PA_SYSMMU_MFC_L, - .end = EXYNOS4_PA_SYSMMU_MFC_L + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [29] = { - .start = IRQ_SYSMMU_MFC_M0_0, - .end = IRQ_SYSMMU_MFC_M0_0, - .flags = IORESOURCE_IRQ, - }, - [30] = { - .start = EXYNOS4_PA_SYSMMU_MFC_R, - .end = EXYNOS4_PA_SYSMMU_MFC_R + SZ_64K - 1, - .flags = IORESOURCE_MEM, - }, - [31] = { - .start = IRQ_SYSMMU_MFC_M1_0, - .end = IRQ_SYSMMU_MFC_M1_0, - .flags = IORESOURCE_IRQ, - }, -}; +static u64 exynos_sysmmu_dma_mask = DMA_BIT_MASK(32); + +#define SYSMMU_PLATFORM_DEVICE(ipname, devid) \ +static struct sysmmu_platform_data platdata_##ipname = { \ + .dbgname = #ipname, \ +}; \ +struct platform_device SYSMMU_PLATDEV(ipname) = \ +{ \ + .name = SYSMMU_DEVNAME_BASE, \ + .id = devid, \ + .dev = { \ + .dma_mask = &exynos_sysmmu_dma_mask, \ + .coherent_dma_mask = DMA_BIT_MASK(32), \ + .platform_data = &platdata_##ipname, \ + }, \ +} + +SYSMMU_PLATFORM_DEVICE(mfc_l, 0); +SYSMMU_PLATFORM_DEVICE(mfc_r, 1); +SYSMMU_PLATFORM_DEVICE(tv, 2); +SYSMMU_PLATFORM_DEVICE(jpeg, 3); +SYSMMU_PLATFORM_DEVICE(rot, 4); +SYSMMU_PLATFORM_DEVICE(fimc0, 5); /* fimc* and gsc* exist exclusively */ +SYSMMU_PLATFORM_DEVICE(fimc1, 6); +SYSMMU_PLATFORM_DEVICE(fimc2, 7); +SYSMMU_PLATFORM_DEVICE(fimc3, 8); +SYSMMU_PLATFORM_DEVICE(gsc0, 5); +SYSMMU_PLATFORM_DEVICE(gsc1, 6); +SYSMMU_PLATFORM_DEVICE(gsc2, 7); +SYSMMU_PLATFORM_DEVICE(gsc3, 8); +SYSMMU_PLATFORM_DEVICE(isp, 9); +SYSMMU_PLATFORM_DEVICE(fimd0, 10); +SYSMMU_PLATFORM_DEVICE(fimd1, 11); +SYSMMU_PLATFORM_DEVICE(camif0, 12); +SYSMMU_PLATFORM_DEVICE(camif1, 13); +SYSMMU_PLATFORM_DEVICE(2d, 14); + +#define SYSMMU_RESOURCE_NAME(core, ipname) sysmmures_##core##_##ipname + +#define SYSMMU_RESOURCE(core, ipname) \ + static struct resource SYSMMU_RESOURCE_NAME(core, ipname)[] __initdata = + +#define DEFINE_SYSMMU_RESOURCE(core, mem, irq) \ + DEFINE_RES_MEM_NAMED(core##_PA_SYSMMU_##mem, SZ_4K, #mem), \ + DEFINE_RES_IRQ_NAMED(core##_IRQ_SYSMMU_##irq##_0, #mem) + +#define SYSMMU_RESOURCE_DEFINE(core, ipname, mem, irq) \ + SYSMMU_RESOURCE(core, ipname) { \ + DEFINE_SYSMMU_RESOURCE(core, mem, irq) \ + } -struct platform_device exynos4_device_sysmmu = { - .name = "s5p-sysmmu", - .id = 32, - .num_resources = ARRAY_SIZE(exynos4_sysmmu_resource), - .resource = exynos4_sysmmu_resource, +struct sysmmu_resource_map { + struct platform_device *pdev; + struct resource *res; + u32 rnum; + struct device *pdd; + char *clocknames; }; -EXPORT_SYMBOL(exynos4_device_sysmmu); -static struct clk *sysmmu_clk[S5P_SYSMMU_TOTAL_IPNUM]; -void sysmmu_clk_init(struct device *dev, sysmmu_ips ips) -{ - sysmmu_clk[ips] = clk_get(dev, sysmmu_ips_name[ips]); - if (IS_ERR(sysmmu_clk[ips])) - sysmmu_clk[ips] = NULL; - else - clk_put(sysmmu_clk[ips]); +#define SYSMMU_RESOURCE_MAPPING(core, ipname, resname) { \ + .pdev = &SYSMMU_PLATDEV(ipname), \ + .res = SYSMMU_RESOURCE_NAME(EXYNOS##core, resname), \ + .rnum = ARRAY_SIZE(SYSMMU_RESOURCE_NAME(EXYNOS##core, resname)),\ + .clocknames = SYSMMU_CLOCK_NAME, \ } -void sysmmu_clk_enable(sysmmu_ips ips) -{ - if (sysmmu_clk[ips]) - clk_enable(sysmmu_clk[ips]); +#define SYSMMU_RESOURCE_MAPPING_MC(core, ipname, resname, pdata) { \ + .pdev = &SYSMMU_PLATDEV(ipname), \ + .res = SYSMMU_RESOURCE_NAME(EXYNOS##core, resname), \ + .rnum = ARRAY_SIZE(SYSMMU_RESOURCE_NAME(EXYNOS##core, resname)),\ + .clocknames = SYSMMU_CLOCK_NAME "," SYSMMU_CLOCK_NAME2, \ +} + +#ifdef CONFIG_EXYNOS_DEV_PD +#define SYSMMU_RESOURCE_MAPPING_PD(core, ipname, resname, pd) { \ + .pdev = &SYSMMU_PLATDEV(ipname), \ + .res = &SYSMMU_RESOURCE_NAME(EXYNOS##core, resname), \ + .rnum = ARRAY_SIZE(SYSMMU_RESOURCE_NAME(EXYNOS##core, resname)),\ + .clocknames = SYSMMU_CLOCK_NAME, \ + .pdd = &exynos##core##_device_pd[pd].dev, \ +} + +#define SYSMMU_RESOURCE_MAPPING_MCPD(core, ipname, resname, pd, pdata) {\ + .pdev = &SYSMMU_PLATDEV(ipname), \ + .res = &SYSMMU_RESOURCE_NAME(EXYNOS##core, resname), \ + .rnum = ARRAY_SIZE(SYSMMU_RESOURCE_NAME(EXYNOS##core, resname)),\ + .clocknames = SYSMMU_CLOCK_NAME "," SYSMMU_CLOCK_NAME2, \ + .pdd = &exynos##core##_device_pd[pd].dev, \ } +#else +#define SYSMMU_RESOURCE_MAPPING_PD(core, ipname, resname, pd) \ + SYSMMU_RESOURCE_MAPPING(core, ipname, resname) +#define SYSMMU_RESOURCE_MAPPING_MCPD(core, ipname, resname, pd, pdata) \ + SYSMMU_RESOURCE_MAPPING_MC(core, ipname, resname, pdata) + +#endif /* CONFIG_EXYNOS_DEV_PD */ + +#ifdef CONFIG_ARCH_EXYNOS4 +SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimc0, FIMC0, FIMC0); +SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimc1, FIMC1, FIMC1); +SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimc2, FIMC2, FIMC2); +SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimc3, FIMC3, FIMC3); +SYSMMU_RESOURCE_DEFINE(EXYNOS4, jpeg, JPEG, JPEG); +SYSMMU_RESOURCE_DEFINE(EXYNOS4, 2d, G2D, 2D); +SYSMMU_RESOURCE_DEFINE(EXYNOS4, tv, TV, TV_M0); +SYSMMU_RESOURCE_DEFINE(EXYNOS4, 2d_acp, 2D_ACP, 2D); +SYSMMU_RESOURCE_DEFINE(EXYNOS4, rot, ROTATOR, ROTATOR); +SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimd0, FIMD0, LCD0_M0); +SYSMMU_RESOURCE_DEFINE(EXYNOS4, fimd1, FIMD1, LCD1_M1); +SYSMMU_RESOURCE_DEFINE(EXYNOS4, flite0, FIMC_LITE0, FIMC_LITE0); +SYSMMU_RESOURCE_DEFINE(EXYNOS4, flite1, FIMC_LITE1, FIMC_LITE1); +SYSMMU_RESOURCE_DEFINE(EXYNOS4, mfc_r, MFC_R, MFC_M0); +SYSMMU_RESOURCE_DEFINE(EXYNOS4, mfc_l, MFC_L, MFC_M1); +SYSMMU_RESOURCE(EXYNOS4, isp) { + DEFINE_SYSMMU_RESOURCE(EXYNOS4, FIMC_ISP, FIMC_ISP), + DEFINE_SYSMMU_RESOURCE(EXYNOS4, FIMC_DRC, FIMC_DRC), + DEFINE_SYSMMU_RESOURCE(EXYNOS4, FIMC_FD, FIMC_FD), + DEFINE_SYSMMU_RESOURCE(EXYNOS4, ISPCPU, FIMC_CX), +}; + +static struct sysmmu_resource_map sysmmu_resmap4[] __initdata = { + SYSMMU_RESOURCE_MAPPING_PD(4, fimc0, fimc0, PD_CAM), + SYSMMU_RESOURCE_MAPPING_PD(4, fimc1, fimc1, PD_CAM), + SYSMMU_RESOURCE_MAPPING_PD(4, fimc2, fimc2, PD_CAM), + SYSMMU_RESOURCE_MAPPING_PD(4, fimc3, fimc3, PD_CAM), + SYSMMU_RESOURCE_MAPPING_PD(4, tv, tv, PD_TV), + SYSMMU_RESOURCE_MAPPING_PD(4, mfc_r, mfc_r, PD_MFC), + SYSMMU_RESOURCE_MAPPING_PD(4, mfc_l, mfc_l, PD_MFC), + SYSMMU_RESOURCE_MAPPING_PD(4, rot, rot, PD_LCD0), + SYSMMU_RESOURCE_MAPPING_PD(4, jpeg, jpeg, PD_CAM), + SYSMMU_RESOURCE_MAPPING_PD(4, fimd0, fimd0, PD_LCD0), +}; + +static struct sysmmu_resource_map sysmmu_resmap4210[] __initdata = { + SYSMMU_RESOURCE_MAPPING_PD(4, 2d, 2d, PD_LCD0), + SYSMMU_RESOURCE_MAPPING_PD(4, fimd1, fimd1, PD_LCD1), +}; + +static struct sysmmu_resource_map sysmmu_resmap4212[] __initdata = { + SYSMMU_RESOURCE_MAPPING(4, 2d, 2d_acp), + SYSMMU_RESOURCE_MAPPING_PD(4, camif0, flite0, PD_ISP), + SYSMMU_RESOURCE_MAPPING_PD(4, camif1, flite1, PD_ISP), + SYSMMU_RESOURCE_MAPPING_PD(4, isp, isp, PD_ISP), +}; +#endif /* CONFIG_ARCH_EXYNOS4 */ -void sysmmu_clk_disable(sysmmu_ips ips) +#ifdef CONFIG_ARCH_EXYNOS5 +SYSMMU_RESOURCE_DEFINE(EXYNOS5, jpeg, JPEG, JPEG); +SYSMMU_RESOURCE_DEFINE(EXYNOS5, fimd1, FIMD1, FIMD1); +SYSMMU_RESOURCE_DEFINE(EXYNOS5, 2d, 2D, 2D); +SYSMMU_RESOURCE_DEFINE(EXYNOS5, rot, ROTATOR, ROTATOR); +SYSMMU_RESOURCE_DEFINE(EXYNOS5, tv, TV, TV); +SYSMMU_RESOURCE_DEFINE(EXYNOS5, flite0, LITE0, LITE0); +SYSMMU_RESOURCE_DEFINE(EXYNOS5, flite1, LITE1, LITE1); +SYSMMU_RESOURCE_DEFINE(EXYNOS5, gsc0, GSC0, GSC0); +SYSMMU_RESOURCE_DEFINE(EXYNOS5, gsc1, GSC1, GSC1); +SYSMMU_RESOURCE_DEFINE(EXYNOS5, gsc2, GSC2, GSC2); +SYSMMU_RESOURCE_DEFINE(EXYNOS5, gsc3, GSC3, GSC3); +SYSMMU_RESOURCE_DEFINE(EXYNOS5, mfc_r, MFC_R, MFC_R); +SYSMMU_RESOURCE_DEFINE(EXYNOS5, mfc_l, MFC_L, MFC_L); +SYSMMU_RESOURCE(EXYNOS5, isp) { + DEFINE_SYSMMU_RESOURCE(EXYNOS5, ISP, ISP), + DEFINE_SYSMMU_RESOURCE(EXYNOS5, DRC, DRC), + DEFINE_SYSMMU_RESOURCE(EXYNOS5, FD, FD), + DEFINE_SYSMMU_RESOURCE(EXYNOS5, ISPCPU, MCUISP), + DEFINE_SYSMMU_RESOURCE(EXYNOS5, SCALERC, SCALERCISP), + DEFINE_SYSMMU_RESOURCE(EXYNOS5, SCALERP, SCALERPISP), + DEFINE_SYSMMU_RESOURCE(EXYNOS5, ODC, ODC), + DEFINE_SYSMMU_RESOURCE(EXYNOS5, DIS0, DIS0), + DEFINE_SYSMMU_RESOURCE(EXYNOS5, DIS1, DIS1), + DEFINE_SYSMMU_RESOURCE(EXYNOS5, 3DNR, 3DNR), +}; + +static struct sysmmu_resource_map sysmmu_resmap5[] __initdata = { + SYSMMU_RESOURCE_MAPPING(5, jpeg, jpeg), + SYSMMU_RESOURCE_MAPPING(5, fimd1, fimd1), + SYSMMU_RESOURCE_MAPPING(5, 2d, 2d), + SYSMMU_RESOURCE_MAPPING(5, rot, rot), + SYSMMU_RESOURCE_MAPPING_PD(5, tv, tv, PD_DISP1), + SYSMMU_RESOURCE_MAPPING_PD(5, camif0, flite0, PD_GSCL), + SYSMMU_RESOURCE_MAPPING_PD(5, camif1, flite1, PD_GSCL), + SYSMMU_RESOURCE_MAPPING_PD(5, gsc0, gsc0, PD_GSCL), + SYSMMU_RESOURCE_MAPPING_PD(5, gsc1, gsc1, PD_GSCL), + SYSMMU_RESOURCE_MAPPING_PD(5, gsc2, gsc2, PD_GSCL), + SYSMMU_RESOURCE_MAPPING_PD(5, gsc3, gsc3, PD_GSCL), + SYSMMU_RESOURCE_MAPPING_PD(5, mfc_r, mfc_r, PD_MFC), + SYSMMU_RESOURCE_MAPPING_PD(5, mfc_l, mfc_l, PD_MFC), + SYSMMU_RESOURCE_MAPPING_MCPD(5, isp, isp, PD_ISP, mc_platdata), +}; +#endif /* CONFIG_ARCH_EXYNOS5 */ + +static int __init init_sysmmu_platform_device(void) { - if (sysmmu_clk[ips]) - clk_disable(sysmmu_clk[ips]); + int i, j; + struct sysmmu_resource_map *resmap[2] = {NULL, NULL}; + int nmap[2] = {0, 0}; + +#ifdef CONFIG_ARCH_EXYNOS5 + if (soc_is_exynos5250()) { + resmap[0] = sysmmu_resmap5; + nmap[0] = ARRAY_SIZE(sysmmu_resmap5); + nmap[1] = 0; + } +#endif + +#ifdef CONFIG_ARCH_EXYNOS4 + if (resmap[0] == NULL) { + resmap[0] = sysmmu_resmap4; + nmap[0] = ARRAY_SIZE(sysmmu_resmap4); + } + + if (soc_is_exynos4210()) { + resmap[1] = sysmmu_resmap4210; + nmap[1] = ARRAY_SIZE(sysmmu_resmap4210); + } + + if (soc_is_exynos4412() || soc_is_exynos4212()) { + resmap[1] = sysmmu_resmap4212; + nmap[1] = ARRAY_SIZE(sysmmu_resmap4212); + } +#endif + + for (j = 0; j < 2; j++) { + for (i = 0; i < nmap[j]; i++) { + struct sysmmu_resource_map *map; + struct sysmmu_platform_data *platdata; + + map = &resmap[j][i]; + + map->pdev->dev.parent = map->pdd; + + platdata = map->pdev->dev.platform_data; + platdata->clockname = map->clocknames; + + if (platform_device_add_resources(map->pdev, map->res, + map->rnum)) { + pr_err("%s: Failed to add device resources for " + "%s.%d\n", __func__, + map->pdev->name, map->pdev->id); + continue; + } + + if (platform_device_register(map->pdev)) { + pr_err("%s: Failed to register %s.%d\n", + __func__, map->pdev->name, + map->pdev->id); + } + } + } + + return 0; } +arch_initcall(init_sysmmu_platform_device); diff --git a/arch/arm/mach-exynos/include/mach/irqs.h b/arch/arm/mach-exynos/include/mach/irqs.h index 9bee8535d9e0..f140e1a2d335 100644 --- a/arch/arm/mach-exynos/include/mach/irqs.h +++ b/arch/arm/mach-exynos/include/mach/irqs.h @@ -154,6 +154,13 @@ #define EXYNOS4_IRQ_SYSMMU_MFC_M1_0 COMBINER_IRQ(5, 6) #define EXYNOS4_IRQ_SYSMMU_PCIE_0 COMBINER_IRQ(5, 7) +#define EXYNOS4_IRQ_SYSMMU_FIMC_LITE0_0 COMBINER_IRQ(16, 0) +#define EXYNOS4_IRQ_SYSMMU_FIMC_LITE1_0 COMBINER_IRQ(16, 1) +#define EXYNOS4_IRQ_SYSMMU_FIMC_ISP_0 COMBINER_IRQ(16, 2) +#define EXYNOS4_IRQ_SYSMMU_FIMC_DRC_0 COMBINER_IRQ(16, 3) +#define EXYNOS4_IRQ_SYSMMU_FIMC_FD_0 COMBINER_IRQ(16, 4) +#define EXYNOS4_IRQ_SYSMMU_FIMC_CX_0 COMBINER_IRQ(16, 5) + #define EXYNOS4_IRQ_FIMD0_FIFO COMBINER_IRQ(11, 0) #define EXYNOS4_IRQ_FIMD0_VSYNC COMBINER_IRQ(11, 1) #define EXYNOS4_IRQ_FIMD0_SYSTEM COMBINER_IRQ(11, 2) @@ -218,24 +225,6 @@ #define IRQ_KEYPAD EXYNOS4_IRQ_KEYPAD #define IRQ_PMU EXYNOS4_IRQ_PMU -#define IRQ_SYSMMU_MDMA0_0 EXYNOS4_IRQ_SYSMMU_MDMA0_0 -#define IRQ_SYSMMU_SSS_0 EXYNOS4_IRQ_SYSMMU_SSS_0 -#define IRQ_SYSMMU_FIMC0_0 EXYNOS4_IRQ_SYSMMU_FIMC0_0 -#define IRQ_SYSMMU_FIMC1_0 EXYNOS4_IRQ_SYSMMU_FIMC1_0 -#define IRQ_SYSMMU_FIMC2_0 EXYNOS4_IRQ_SYSMMU_FIMC2_0 -#define IRQ_SYSMMU_FIMC3_0 EXYNOS4_IRQ_SYSMMU_FIMC3_0 -#define IRQ_SYSMMU_JPEG_0 EXYNOS4_IRQ_SYSMMU_JPEG_0 -#define IRQ_SYSMMU_2D_0 EXYNOS4_IRQ_SYSMMU_2D_0 - -#define IRQ_SYSMMU_ROTATOR_0 EXYNOS4_IRQ_SYSMMU_ROTATOR_0 -#define IRQ_SYSMMU_MDMA1_0 EXYNOS4_IRQ_SYSMMU_MDMA1_0 -#define IRQ_SYSMMU_LCD0_M0_0 EXYNOS4_IRQ_SYSMMU_LCD0_M0_0 -#define IRQ_SYSMMU_LCD1_M1_0 EXYNOS4_IRQ_SYSMMU_LCD1_M1_0 -#define IRQ_SYSMMU_TV_M0_0 EXYNOS4_IRQ_SYSMMU_TV_M0_0 -#define IRQ_SYSMMU_MFC_M0_0 EXYNOS4_IRQ_SYSMMU_MFC_M0_0 -#define IRQ_SYSMMU_MFC_M1_0 EXYNOS4_IRQ_SYSMMU_MFC_M1_0 -#define IRQ_SYSMMU_PCIE_0 EXYNOS4_IRQ_SYSMMU_PCIE_0 - #define IRQ_FIMD0_FIFO EXYNOS4_IRQ_FIMD0_FIFO #define IRQ_FIMD0_VSYNC EXYNOS4_IRQ_FIMD0_VSYNC #define IRQ_FIMD0_SYSTEM EXYNOS4_IRQ_FIMD0_SYSTEM diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h index 024d38ff1718..69f2ea6fb0d2 100644 --- a/arch/arm/mach-exynos/include/mach/map.h +++ b/arch/arm/mach-exynos/include/mach/map.h @@ -91,6 +91,7 @@ #define EXYNOS4_PA_PDMA1 0x12690000 #define EXYNOS4_PA_SYSMMU_MDMA 0x10A40000 +#define EXYNOS4_PA_SYSMMU_2D_ACP 0x10A40000 #define EXYNOS4_PA_SYSMMU_SSS 0x10A50000 #define EXYNOS4_PA_SYSMMU_FIMC0 0x11A20000 #define EXYNOS4_PA_SYSMMU_FIMC1 0x11A30000 @@ -99,6 +100,12 @@ #define EXYNOS4_PA_SYSMMU_JPEG 0x11A60000 #define EXYNOS4_PA_SYSMMU_FIMD0 0x11E20000 #define EXYNOS4_PA_SYSMMU_FIMD1 0x12220000 +#define EXYNOS4_PA_SYSMMU_FIMC_ISP 0x12260000 +#define EXYNOS4_PA_SYSMMU_FIMC_DRC 0x12270000 +#define EXYNOS4_PA_SYSMMU_FIMC_FD 0x122A0000 +#define EXYNOS4_PA_SYSMMU_ISPCPU 0x122B0000 +#define EXYNOS4_PA_SYSMMU_FIMC_LITE0 0x123B0000 +#define EXYNOS4_PA_SYSMMU_FIMC_LITE1 0x123C0000 #define EXYNOS4_PA_SYSMMU_PCIe 0x12620000 #define EXYNOS4_PA_SYSMMU_G2D 0x12A20000 #define EXYNOS4_PA_SYSMMU_ROTATOR 0x12A30000 @@ -106,6 +113,37 @@ #define EXYNOS4_PA_SYSMMU_TV 0x12E20000 #define EXYNOS4_PA_SYSMMU_MFC_L 0x13620000 #define EXYNOS4_PA_SYSMMU_MFC_R 0x13630000 + +#define EXYNOS5_PA_SYSMMU_MDMA1 0x10A40000 +#define EXYNOS5_PA_SYSMMU_SSS 0x10A50000 +#define EXYNOS5_PA_SYSMMU_2D 0x10A60000 +#define EXYNOS5_PA_SYSMMU_MFC_L 0x11200000 +#define EXYNOS5_PA_SYSMMU_MFC_R 0x11210000 +#define EXYNOS5_PA_SYSMMU_ROTATOR 0x11D40000 +#define EXYNOS5_PA_SYSMMU_MDMA2 0x11D50000 +#define EXYNOS5_PA_SYSMMU_JPEG 0x11F20000 +#define EXYNOS5_PA_SYSMMU_IOP 0x12360000 +#define EXYNOS5_PA_SYSMMU_RTIC 0x12370000 +#define EXYNOS5_PA_SYSMMU_GPS 0x12630000 +#define EXYNOS5_PA_SYSMMU_ISP 0x13260000 +#define EXYNOS5_PA_SYSMMU_DRC 0x12370000 +#define EXYNOS5_PA_SYSMMU_SCALERC 0x13280000 +#define EXYNOS5_PA_SYSMMU_SCALERP 0x13290000 +#define EXYNOS5_PA_SYSMMU_FD 0x132A0000 +#define EXYNOS5_PA_SYSMMU_ISPCPU 0x132B0000 +#define EXYNOS5_PA_SYSMMU_ODC 0x132C0000 +#define EXYNOS5_PA_SYSMMU_DIS0 0x132D0000 +#define EXYNOS5_PA_SYSMMU_DIS1 0x132E0000 +#define EXYNOS5_PA_SYSMMU_3DNR 0x132F0000 +#define EXYNOS5_PA_SYSMMU_LITE0 0x13C40000 +#define EXYNOS5_PA_SYSMMU_LITE1 0x13C50000 +#define EXYNOS5_PA_SYSMMU_GSC0 0x13E80000 +#define EXYNOS5_PA_SYSMMU_GSC1 0x13E90000 +#define EXYNOS5_PA_SYSMMU_GSC2 0x13EA0000 +#define EXYNOS5_PA_SYSMMU_GSC3 0x13EB0000 +#define EXYNOS5_PA_SYSMMU_FIMD1 0x14640000 +#define EXYNOS5_PA_SYSMMU_TV 0x14650000 + #define EXYNOS4_PA_SPI0 0x13920000 #define EXYNOS4_PA_SPI1 0x13930000 #define EXYNOS4_PA_SPI2 0x13940000 diff --git a/arch/arm/mach-exynos/include/mach/regs-clock.h b/arch/arm/mach-exynos/include/mach/regs-clock.h index e141c1fd68d8..7395236ffc0e 100644 --- a/arch/arm/mach-exynos/include/mach/regs-clock.h +++ b/arch/arm/mach-exynos/include/mach/regs-clock.h @@ -135,6 +135,9 @@ #define EXYNOS4_CLKGATE_SCLKCPU EXYNOS_CLKREG(0x14800) #define EXYNOS4_CLKGATE_IP_CPU EXYNOS_CLKREG(0x14900) +#define EXYNOS4_CLKGATE_IP_ISP0 EXYNOS_CLKREG(0x18800) +#define EXYNOS4_CLKGATE_IP_ISP1 EXYNOS_CLKREG(0x18804) + #define EXYNOS4_APLL_LOCKTIME (0x1C20) /* 300us */ #define EXYNOS4_APLLCON0_ENABLE_SHIFT (31) @@ -297,6 +300,8 @@ #define EXYNOS5_CLKDIV_PERIC0 EXYNOS_CLKREG(0x10558) #define EXYNOS5_CLKGATE_IP_ACP EXYNOS_CLKREG(0x08800) +#define EXYNOS5_CLKGATE_IP_ISP0 EXYNOS_CLKREG(0x0C800) +#define EXYNOS5_CLKGATE_IP_ISP1 EXYNOS_CLKREG(0x0C804) #define EXYNOS5_CLKGATE_IP_GSCL EXYNOS_CLKREG(0x10920) #define EXYNOS5_CLKGATE_IP_DISP1 EXYNOS_CLKREG(0x10928) #define EXYNOS5_CLKGATE_IP_MFC EXYNOS_CLKREG(0x1092C) diff --git a/arch/arm/mach-exynos/include/mach/regs-sysmmu.h b/arch/arm/mach-exynos/include/mach/regs-sysmmu.h deleted file mode 100644 index 68ff6ad08a2b..000000000000 --- a/arch/arm/mach-exynos/include/mach/regs-sysmmu.h +++ /dev/null @@ -1,28 +0,0 @@ -/* linux/arch/arm/mach-exynos4/include/mach/regs-sysmmu.h - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * - * EXYNOS4 - System MMU register - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. -*/ - -#ifndef __ASM_ARCH_REGS_SYSMMU_H -#define __ASM_ARCH_REGS_SYSMMU_H __FILE__ - -#define S5P_MMU_CTRL 0x000 -#define S5P_MMU_CFG 0x004 -#define S5P_MMU_STATUS 0x008 -#define S5P_MMU_FLUSH 0x00C -#define S5P_PT_BASE_ADDR 0x014 -#define S5P_INT_STATUS 0x018 -#define S5P_INT_CLEAR 0x01C -#define S5P_PAGE_FAULT_ADDR 0x024 -#define S5P_AW_FAULT_ADDR 0x028 -#define S5P_AR_FAULT_ADDR 0x02C -#define S5P_DEFAULT_SLAVE_ADDR 0x030 - -#endif /* __ASM_ARCH_REGS_SYSMMU_H */ diff --git a/arch/arm/mach-exynos/include/mach/sysmmu.h b/arch/arm/mach-exynos/include/mach/sysmmu.h index 6a5fbb534e82..998daf2add92 100644 --- a/arch/arm/mach-exynos/include/mach/sysmmu.h +++ b/arch/arm/mach-exynos/include/mach/sysmmu.h @@ -1,46 +1,66 @@ -/* linux/arch/arm/mach-exynos4/include/mach/sysmmu.h - * - * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. +/* + * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd. * http://www.samsung.com * - * Samsung sysmmu driver for EXYNOS4 + * EXYNOS - System MMU support * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. -*/ - -#ifndef __ASM_ARM_ARCH_SYSMMU_H -#define __ASM_ARM_ARCH_SYSMMU_H __FILE__ - -enum exynos4_sysmmu_ips { - SYSMMU_MDMA, - SYSMMU_SSS, - SYSMMU_FIMC0, - SYSMMU_FIMC1, - SYSMMU_FIMC2, - SYSMMU_FIMC3, - SYSMMU_JPEG, - SYSMMU_FIMD0, - SYSMMU_FIMD1, - SYSMMU_PCIe, - SYSMMU_G2D, - SYSMMU_ROTATOR, - SYSMMU_MDMA2, - SYSMMU_TV, - SYSMMU_MFC_L, - SYSMMU_MFC_R, - EXYNOS4_SYSMMU_TOTAL_IPNUM, + */ + +#ifndef _ARM_MACH_EXYNOS_SYSMMU_H_ +#define _ARM_MACH_EXYNOS_SYSMMU_H_ + +struct sysmmu_platform_data { + char *dbgname; + /* comma(,) separated list of clock names for clock gating */ + char *clockname; }; -#define S5P_SYSMMU_TOTAL_IPNUM EXYNOS4_SYSMMU_TOTAL_IPNUM +#define SYSMMU_DEVNAME_BASE "exynos-sysmmu" + +#define SYSMMU_CLOCK_NAME "sysmmu" +#define SYSMMU_CLOCK_NAME2 "sysmmu_mc" + +#ifdef CONFIG_EXYNOS_DEV_SYSMMU +#include +struct platform_device; + +#define SYSMMU_PLATDEV(ipname) exynos_device_sysmmu_##ipname + +extern struct platform_device SYSMMU_PLATDEV(mfc_l); +extern struct platform_device SYSMMU_PLATDEV(mfc_r); +extern struct platform_device SYSMMU_PLATDEV(tv); +extern struct platform_device SYSMMU_PLATDEV(jpeg); +extern struct platform_device SYSMMU_PLATDEV(rot); +extern struct platform_device SYSMMU_PLATDEV(fimc0); +extern struct platform_device SYSMMU_PLATDEV(fimc1); +extern struct platform_device SYSMMU_PLATDEV(fimc2); +extern struct platform_device SYSMMU_PLATDEV(fimc3); +extern struct platform_device SYSMMU_PLATDEV(gsc0); +extern struct platform_device SYSMMU_PLATDEV(gsc1); +extern struct platform_device SYSMMU_PLATDEV(gsc2); +extern struct platform_device SYSMMU_PLATDEV(gsc3); +extern struct platform_device SYSMMU_PLATDEV(isp); +extern struct platform_device SYSMMU_PLATDEV(fimd0); +extern struct platform_device SYSMMU_PLATDEV(fimd1); +extern struct platform_device SYSMMU_PLATDEV(camif0); +extern struct platform_device SYSMMU_PLATDEV(camif1); +extern struct platform_device SYSMMU_PLATDEV(2d); -extern const char *sysmmu_ips_name[EXYNOS4_SYSMMU_TOTAL_IPNUM]; +#ifdef CONFIG_IOMMU_API +static inline void platform_set_sysmmu( + struct device *sysmmu, struct device *dev) +{ + dev->archdata.iommu = sysmmu; +} +#endif -typedef enum exynos4_sysmmu_ips sysmmu_ips; +#else /* !CONFIG_EXYNOS_DEV_SYSMMU */ +#define platform_set_sysmmu(dev, sysmmu) do { } while (0) +#endif -void sysmmu_clk_init(struct device *dev, sysmmu_ips ips); -void sysmmu_clk_enable(sysmmu_ips ips); -void sysmmu_clk_disable(sysmmu_ips ips); +#define SYSMMU_CLOCK_DEVNAME(ipname, id) (SYSMMU_DEVNAME_BASE "." #id) -#endif /* __ASM_ARM_ARCH_SYSMMU_H */ +#endif /* _ARM_MACH_EXYNOS_SYSMMU_H_ */ diff --git a/arch/arm/mach-exynos/mach-armlex4210.c b/arch/arm/mach-exynos/mach-armlex4210.c index d726fcd3acf9..6ce21484501e 100644 --- a/arch/arm/mach-exynos/mach-armlex4210.c +++ b/arch/arm/mach-exynos/mach-armlex4210.c @@ -157,7 +157,6 @@ static struct platform_device *armlex4210_devices[] __initdata = { &s3c_device_hsmmc3, &s3c_device_rtc, &s3c_device_wdt, - &exynos4_device_sysmmu, &samsung_asoc_dma, &armlex4210_smsc911x, &exynos4_device_ahci, diff --git a/arch/arm/mach-exynos/mach-smdkv310.c b/arch/arm/mach-exynos/mach-smdkv310.c index 83b91fa777c1..495c7e502be1 100644 --- a/arch/arm/mach-exynos/mach-smdkv310.c +++ b/arch/arm/mach-exynos/mach-smdkv310.c @@ -281,7 +281,6 @@ static struct platform_device *smdkv310_devices[] __initdata = { &s5p_device_mfc_l, &s5p_device_mfc_r, &exynos4_device_spdif, - &exynos4_device_sysmmu, &samsung_asoc_dma, &samsung_asoc_idma, &s5p_device_fimd0, -- cgit v1.2.3 From 9c75fc8c5c8c50775fc8b89418219221335b758f Mon Sep 17 00:00:00 2001 From: Jesper Nilsson Date: Thu, 5 Apr 2012 11:52:55 +0200 Subject: CRIS: Remove legacy RTC drivers These old drivers are not used anymore, we use the ones in drivers/rtc. This allows us to remove some cruft in the CRIS timekeeping code. Signed-off-by: Jesper Nilsson --- arch/cris/arch-v10/drivers/ds1302.c | 515 ---------------------------------- arch/cris/arch-v10/drivers/pcf8563.c | 380 ------------------------- arch/cris/arch-v10/kernel/fasttimer.c | 2 - arch/cris/arch-v10/kernel/time.c | 9 - arch/cris/arch-v32/kernel/time.c | 7 - arch/cris/include/asm/Kbuild | 1 - arch/cris/include/asm/rtc.h | 107 ------- arch/cris/kernel/time.c | 76 +---- 8 files changed, 7 insertions(+), 1090 deletions(-) delete mode 100644 arch/cris/arch-v10/drivers/ds1302.c delete mode 100644 arch/cris/arch-v10/drivers/pcf8563.c delete mode 100644 arch/cris/include/asm/rtc.h (limited to 'arch') diff --git a/arch/cris/arch-v10/drivers/ds1302.c b/arch/cris/arch-v10/drivers/ds1302.c deleted file mode 100644 index 74f99c688c8d..000000000000 --- a/arch/cris/arch-v10/drivers/ds1302.c +++ /dev/null @@ -1,515 +0,0 @@ -/*!*************************************************************************** -*! -*! FILE NAME : ds1302.c -*! -*! DESCRIPTION: Implements an interface for the DS1302 RTC through Etrax I/O -*! -*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init -*! -*! --------------------------------------------------------------------------- -*! -*! (C) Copyright 1999-2007 Axis Communications AB, LUND, SWEDEN -*! -*!***************************************************************************/ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "i2c.h" - -#define RTC_MAJOR_NR 121 /* local major, change later */ - -static DEFINE_MUTEX(ds1302_mutex); -static const char ds1302_name[] = "ds1302"; - -/* The DS1302 might be connected to different bits on different products. - * It has three signals - SDA, SCL and RST. RST and SCL are always outputs, - * but SDA can have a selected direction. - * For now, only PORT_PB is hardcoded. - */ - -/* The RST bit may be on either the Generic Port or Port PB. */ -#ifdef CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT -#define TK_RST_OUT(x) REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, CONFIG_ETRAX_DS1302_RSTBIT, x) -#define TK_RST_DIR(x) -#else -#define TK_RST_OUT(x) REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, CONFIG_ETRAX_DS1302_RSTBIT, x) -#define TK_RST_DIR(x) REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, CONFIG_ETRAX_DS1302_RSTBIT, x) -#endif - - -#define TK_SDA_OUT(x) REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, CONFIG_ETRAX_DS1302_SDABIT, x) -#define TK_SCL_OUT(x) REG_SHADOW_SET(R_PORT_PB_DATA, port_pb_data_shadow, CONFIG_ETRAX_DS1302_SCLBIT, x) - -#define TK_SDA_IN() ((*R_PORT_PB_READ >> CONFIG_ETRAX_DS1302_SDABIT) & 1) -/* 1 is out, 0 is in */ -#define TK_SDA_DIR(x) REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, CONFIG_ETRAX_DS1302_SDABIT, x) -#define TK_SCL_DIR(x) REG_SHADOW_SET(R_PORT_PB_DIR, port_pb_dir_shadow, CONFIG_ETRAX_DS1302_SCLBIT, x) - - -/* - * The reason for tempudelay and not udelay is that loops_per_usec - * (used in udelay) is not set when functions here are called from time.c - */ - -static void tempudelay(int usecs) -{ - volatile int loops; - - for(loops = usecs * 12; loops > 0; loops--) - /* nothing */; -} - - -/* Send 8 bits. */ -static void -out_byte(unsigned char x) -{ - int i; - TK_SDA_DIR(1); - for (i = 8; i--;) { - /* The chip latches incoming bits on the rising edge of SCL. */ - TK_SCL_OUT(0); - TK_SDA_OUT(x & 1); - tempudelay(1); - TK_SCL_OUT(1); - tempudelay(1); - x >>= 1; - } - TK_SDA_DIR(0); -} - -static unsigned char -in_byte(void) -{ - unsigned char x = 0; - int i; - - /* Read byte. Bits come LSB first, on the falling edge of SCL. - * Assume SDA is in input direction already. - */ - TK_SDA_DIR(0); - - for (i = 8; i--;) { - TK_SCL_OUT(0); - tempudelay(1); - x >>= 1; - x |= (TK_SDA_IN() << 7); - TK_SCL_OUT(1); - tempudelay(1); - } - - return x; -} - -/* Prepares for a transaction by de-activating RST (active-low). */ - -static void -start(void) -{ - TK_SCL_OUT(0); - tempudelay(1); - TK_RST_OUT(0); - tempudelay(5); - TK_RST_OUT(1); -} - -/* Ends a transaction by taking RST active again. */ - -static void -stop(void) -{ - tempudelay(2); - TK_RST_OUT(0); -} - -/* Enable writing. */ - -static void -ds1302_wenable(void) -{ - start(); - out_byte(0x8e); /* Write control register */ - out_byte(0x00); /* Disable write protect bit 7 = 0 */ - stop(); -} - -/* Disable writing. */ - -static void -ds1302_wdisable(void) -{ - start(); - out_byte(0x8e); /* Write control register */ - out_byte(0x80); /* Disable write protect bit 7 = 0 */ - stop(); -} - - - -/* Read a byte from the selected register in the DS1302. */ - -unsigned char -ds1302_readreg(int reg) -{ - unsigned char x; - - start(); - out_byte(0x81 | (reg << 1)); /* read register */ - x = in_byte(); - stop(); - - return x; -} - -/* Write a byte to the selected register. */ - -void -ds1302_writereg(int reg, unsigned char val) -{ -#ifndef CONFIG_ETRAX_RTC_READONLY - int do_writereg = 1; -#else - int do_writereg = 0; - - if (reg == RTC_TRICKLECHARGER) - do_writereg = 1; -#endif - - if (do_writereg) { - ds1302_wenable(); - start(); - out_byte(0x80 | (reg << 1)); /* write register */ - out_byte(val); - stop(); - ds1302_wdisable(); - } -} - -void -get_rtc_time(struct rtc_time *rtc_tm) -{ - unsigned long flags; - - local_irq_save(flags); - - rtc_tm->tm_sec = CMOS_READ(RTC_SECONDS); - rtc_tm->tm_min = CMOS_READ(RTC_MINUTES); - rtc_tm->tm_hour = CMOS_READ(RTC_HOURS); - rtc_tm->tm_mday = CMOS_READ(RTC_DAY_OF_MONTH); - rtc_tm->tm_mon = CMOS_READ(RTC_MONTH); - rtc_tm->tm_year = CMOS_READ(RTC_YEAR); - - local_irq_restore(flags); - - rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); - rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min); - rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour); - rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday); - rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon); - rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year); - - /* - * Account for differences between how the RTC uses the values - * and how they are defined in a struct rtc_time; - */ - - if (rtc_tm->tm_year <= 69) - rtc_tm->tm_year += 100; - - rtc_tm->tm_mon--; -} - -static unsigned char days_in_mo[] = - {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - -/* ioctl that supports RTC_RD_TIME and RTC_SET_TIME (read and set time/date). */ - -static int rtc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - unsigned long flags; - - switch(cmd) { - case RTC_RD_TIME: /* read the time/date from RTC */ - { - struct rtc_time rtc_tm; - - memset(&rtc_tm, 0, sizeof (struct rtc_time)); - get_rtc_time(&rtc_tm); - if (copy_to_user((struct rtc_time*)arg, &rtc_tm, sizeof(struct rtc_time))) - return -EFAULT; - return 0; - } - - case RTC_SET_TIME: /* set the RTC */ - { - struct rtc_time rtc_tm; - unsigned char mon, day, hrs, min, sec, leap_yr; - unsigned int yrs; - - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if (copy_from_user(&rtc_tm, (struct rtc_time*)arg, sizeof(struct rtc_time))) - return -EFAULT; - - yrs = rtc_tm.tm_year + 1900; - mon = rtc_tm.tm_mon + 1; /* tm_mon starts at zero */ - day = rtc_tm.tm_mday; - hrs = rtc_tm.tm_hour; - min = rtc_tm.tm_min; - sec = rtc_tm.tm_sec; - - - if ((yrs < 1970) || (yrs > 2069)) - return -EINVAL; - - leap_yr = ((!(yrs % 4) && (yrs % 100)) || !(yrs % 400)); - - if ((mon > 12) || (day == 0)) - return -EINVAL; - - if (day > (days_in_mo[mon] + ((mon == 2) && leap_yr))) - return -EINVAL; - - if ((hrs >= 24) || (min >= 60) || (sec >= 60)) - return -EINVAL; - - if (yrs >= 2000) - yrs -= 2000; /* RTC (0, 1, ... 69) */ - else - yrs -= 1900; /* RTC (70, 71, ... 99) */ - - sec = bin2bcd(sec); - min = bin2bcd(min); - hrs = bin2bcd(hrs); - day = bin2bcd(day); - mon = bin2bcd(mon); - yrs = bin2bcd(yrs); - - local_irq_save(flags); - CMOS_WRITE(yrs, RTC_YEAR); - CMOS_WRITE(mon, RTC_MONTH); - CMOS_WRITE(day, RTC_DAY_OF_MONTH); - CMOS_WRITE(hrs, RTC_HOURS); - CMOS_WRITE(min, RTC_MINUTES); - CMOS_WRITE(sec, RTC_SECONDS); - local_irq_restore(flags); - - /* Notice that at this point, the RTC is updated but - * the kernel is still running with the old time. - * You need to set that separately with settimeofday - * or adjtimex. - */ - return 0; - } - - case RTC_SET_CHARGE: /* set the RTC TRICKLE CHARGE register */ - { - int tcs_val; - - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if(copy_from_user(&tcs_val, (int*)arg, sizeof(int))) - return -EFAULT; - - tcs_val = RTC_TCR_PATTERN | (tcs_val & 0x0F); - ds1302_writereg(RTC_TRICKLECHARGER, tcs_val); - return 0; - } - case RTC_VL_READ: - { - /* TODO: - * Implement voltage low detection support - */ - printk(KERN_WARNING "DS1302: RTC Voltage Low detection" - " is not supported\n"); - return 0; - } - case RTC_VL_CLR: - { - /* TODO: - * Nothing to do since Voltage Low detection is not supported - */ - return 0; - } - default: - return -ENOIOCTLCMD; - } -} - -static long rtc_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int ret; - - mutex_lock(&ds1302_mutex); - ret = rtc_ioctl(file, cmd, arg); - mutex_unlock(&ds1302_mutex); - - return ret; -} - -static void -print_rtc_status(void) -{ - struct rtc_time tm; - - get_rtc_time(&tm); - - /* - * There is no way to tell if the luser has the RTC set for local - * time or for Universal Standard Time (GMT). Probably local though. - */ - - printk(KERN_INFO "rtc_time\t: %02d:%02d:%02d\n", - tm.tm_hour, tm.tm_min, tm.tm_sec); - printk(KERN_INFO "rtc_date\t: %04d-%02d-%02d\n", - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); -} - -/* The various file operations we support. */ - -static const struct file_operations rtc_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = rtc_unlocked_ioctl, - .llseek = noop_llseek, -}; - -/* Probe for the chip by writing something to its RAM and try reading it back. */ - -#define MAGIC_PATTERN 0x42 - -static int __init -ds1302_probe(void) -{ - int retval, res; - - TK_RST_DIR(1); - TK_SCL_DIR(1); - TK_SDA_DIR(0); - - /* Try to talk to timekeeper. */ - - ds1302_wenable(); - start(); - out_byte(0xc0); /* write RAM byte 0 */ - out_byte(MAGIC_PATTERN); /* write something magic */ - start(); - out_byte(0xc1); /* read RAM byte 0 */ - - if((res = in_byte()) == MAGIC_PATTERN) { - stop(); - ds1302_wdisable(); - printk(KERN_INFO "%s: RTC found.\n", ds1302_name); - printk(KERN_INFO "%s: SDA, SCL, RST on PB%i, PB%i, %s%i\n", - ds1302_name, - CONFIG_ETRAX_DS1302_SDABIT, - CONFIG_ETRAX_DS1302_SCLBIT, -#ifdef CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT - "GENIO", -#else - "PB", -#endif - CONFIG_ETRAX_DS1302_RSTBIT); - print_rtc_status(); - retval = 1; - } else { - stop(); - retval = 0; - } - - return retval; -} - - -/* Just probe for the RTC and register the device to handle the ioctl needed. */ - -int __init -ds1302_init(void) -{ -#ifdef CONFIG_ETRAX_I2C - i2c_init(); -#endif - - if (!ds1302_probe()) { -#ifdef CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT -#if CONFIG_ETRAX_DS1302_RSTBIT == 27 - /* - * The only way to set g27 to output is to enable ATA. - * - * Make sure that R_GEN_CONFIG is setup correct. - */ - /* Allocating the ATA interface will grab almost all - * pins in I/O groups a, b, c and d. A consequence of - * allocating the ATA interface is that the fixed - * interfaces shared RAM, parallel port 0, parallel - * port 1, parallel port W, SCSI-8 port 0, SCSI-8 port - * 1, SCSI-W, serial port 2, serial port 3, - * synchronous serial port 3 and USB port 2 and almost - * all GPIO pins on port g cannot be used. - */ - if (cris_request_io_interface(if_ata, "ds1302/ATA")) { - printk(KERN_WARNING "ds1302: Failed to get IO interface\n"); - return -1; - } - -#elif CONFIG_ETRAX_DS1302_RSTBIT == 0 - if (cris_io_interface_allocate_pins(if_gpio_grp_a, - 'g', - CONFIG_ETRAX_DS1302_RSTBIT, - CONFIG_ETRAX_DS1302_RSTBIT)) { - printk(KERN_WARNING "ds1302: Failed to get IO interface\n"); - return -1; - } - - /* Set the direction of this bit to out. */ - genconfig_shadow = ((genconfig_shadow & - ~IO_MASK(R_GEN_CONFIG, g0dir)) | - (IO_STATE(R_GEN_CONFIG, g0dir, out))); - *R_GEN_CONFIG = genconfig_shadow; -#endif - if (!ds1302_probe()) { - printk(KERN_WARNING "%s: RTC not found.\n", ds1302_name); - return -1; - } -#else - printk(KERN_WARNING "%s: RTC not found.\n", ds1302_name); - return -1; -#endif - } - /* Initialise trickle charger */ - ds1302_writereg(RTC_TRICKLECHARGER, - RTC_TCR_PATTERN |(CONFIG_ETRAX_DS1302_TRICKLE_CHARGE & 0x0F)); - /* Start clock by resetting CLOCK_HALT */ - ds1302_writereg(RTC_SECONDS, (ds1302_readreg(RTC_SECONDS) & 0x7F)); - return 0; -} - -static int __init ds1302_register(void) -{ - ds1302_init(); - if (register_chrdev(RTC_MAJOR_NR, ds1302_name, &rtc_fops)) { - printk(KERN_INFO "%s: unable to get major %d for rtc\n", - ds1302_name, RTC_MAJOR_NR); - return -1; - } - return 0; - -} - -module_init(ds1302_register); diff --git a/arch/cris/arch-v10/drivers/pcf8563.c b/arch/cris/arch-v10/drivers/pcf8563.c deleted file mode 100644 index 9da056860c92..000000000000 --- a/arch/cris/arch-v10/drivers/pcf8563.c +++ /dev/null @@ -1,380 +0,0 @@ -/* - * PCF8563 RTC - * - * From Phillips' datasheet: - * - * The PCF8563 is a CMOS real-time clock/calendar optimized for low power - * consumption. A programmable clock output, interrupt output and voltage - * low detector are also provided. All address and data are transferred - * serially via two-line bidirectional I2C-bus. Maximum bus speed is - * 400 kbits/s. The built-in word address register is incremented - * automatically after each written or read byte. - * - * Copyright (c) 2002-2007, Axis Communications AB - * All rights reserved. - * - * Author: Tobias Anderberg . - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "i2c.h" - -#define PCF8563_MAJOR 121 /* Local major number. */ -#define DEVICE_NAME "rtc" /* Name which is registered in /proc/devices. */ -#define PCF8563_NAME "PCF8563" -#define DRIVER_VERSION "$Revision: 1.24 $" - -/* I2C bus slave registers. */ -#define RTC_I2C_READ 0xa3 -#define RTC_I2C_WRITE 0xa2 - -/* Two simple wrapper macros, saves a few keystrokes. */ -#define rtc_read(x) i2c_readreg(RTC_I2C_READ, x) -#define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y) - -static DEFINE_MUTEX(pcf8563_mutex); -static DEFINE_MUTEX(rtc_lock); /* Protect state etc */ - -static const unsigned char days_in_month[] = - { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - -static long pcf8563_unlocked_ioctl(struct file *, unsigned int, unsigned long); - -/* Cache VL bit value read at driver init since writing the RTC_SECOND - * register clears the VL status. - */ -static int voltage_low; - -static const struct file_operations pcf8563_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = pcf8563_unlocked_ioctl, - .llseek = noop_llseek, -}; - -unsigned char -pcf8563_readreg(int reg) -{ - unsigned char res = rtc_read(reg); - - /* The PCF8563 does not return 0 for unimplemented bits. */ - switch (reg) { - case RTC_SECONDS: - case RTC_MINUTES: - res &= 0x7F; - break; - case RTC_HOURS: - case RTC_DAY_OF_MONTH: - res &= 0x3F; - break; - case RTC_WEEKDAY: - res &= 0x07; - break; - case RTC_MONTH: - res &= 0x1F; - break; - case RTC_CONTROL1: - res &= 0xA8; - break; - case RTC_CONTROL2: - res &= 0x1F; - break; - case RTC_CLOCKOUT_FREQ: - case RTC_TIMER_CONTROL: - res &= 0x83; - break; - } - return res; -} - -void -pcf8563_writereg(int reg, unsigned char val) -{ - rtc_write(reg, val); -} - -void -get_rtc_time(struct rtc_time *tm) -{ - tm->tm_sec = rtc_read(RTC_SECONDS); - tm->tm_min = rtc_read(RTC_MINUTES); - tm->tm_hour = rtc_read(RTC_HOURS); - tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH); - tm->tm_wday = rtc_read(RTC_WEEKDAY); - tm->tm_mon = rtc_read(RTC_MONTH); - tm->tm_year = rtc_read(RTC_YEAR); - - if (tm->tm_sec & 0x80) { - printk(KERN_ERR "%s: RTC Voltage Low - reliable date/time " - "information is no longer guaranteed!\n", PCF8563_NAME); - } - - tm->tm_year = bcd2bin(tm->tm_year) + - ((tm->tm_mon & 0x80) ? 100 : 0); - tm->tm_sec &= 0x7F; - tm->tm_min &= 0x7F; - tm->tm_hour &= 0x3F; - tm->tm_mday &= 0x3F; - tm->tm_wday &= 0x07; /* Not coded in BCD. */ - tm->tm_mon &= 0x1F; - - tm->tm_sec = bcd2bin(tm->tm_sec); - tm->tm_min = bcd2bin(tm->tm_min); - tm->tm_hour = bcd2bin(tm->tm_hour); - tm->tm_mday = bcd2bin(tm->tm_mday); - tm->tm_mon = bcd2bin(tm->tm_mon); - tm->tm_mon--; /* Month is 1..12 in RTC but 0..11 in linux */ -} - -int __init -pcf8563_init(void) -{ - static int res; - static int first = 1; - - if (!first) - return res; - first = 0; - - /* Initiate the i2c protocol. */ - res = i2c_init(); - if (res < 0) { - printk(KERN_CRIT "pcf8563_init: Failed to init i2c.\n"); - return res; - } - - /* - * First of all we need to reset the chip. This is done by - * clearing control1, control2 and clk freq and resetting - * all alarms. - */ - if (rtc_write(RTC_CONTROL1, 0x00) < 0) - goto err; - - if (rtc_write(RTC_CONTROL2, 0x00) < 0) - goto err; - - if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0) - goto err; - - if (rtc_write(RTC_TIMER_CONTROL, 0x03) < 0) - goto err; - - /* Reset the alarms. */ - if (rtc_write(RTC_MINUTE_ALARM, 0x80) < 0) - goto err; - - if (rtc_write(RTC_HOUR_ALARM, 0x80) < 0) - goto err; - - if (rtc_write(RTC_DAY_ALARM, 0x80) < 0) - goto err; - - if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0) - goto err; - - /* Check for low voltage, and warn about it. */ - if (rtc_read(RTC_SECONDS) & 0x80) { - voltage_low = 1; - printk(KERN_WARNING "%s: RTC Voltage Low - reliable " - "date/time information is no longer guaranteed!\n", - PCF8563_NAME); - } - - return res; - -err: - printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME); - res = -1; - return res; -} - -void __exit -pcf8563_exit(void) -{ - unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME); -} - -/* - * ioctl calls for this driver. Why return -ENOTTY upon error? Because - * POSIX says so! - */ -static int pcf8563_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - /* Some sanity checks. */ - if (_IOC_TYPE(cmd) != RTC_MAGIC) - return -ENOTTY; - - if (_IOC_NR(cmd) > RTC_MAX_IOCTL) - return -ENOTTY; - - switch (cmd) { - case RTC_RD_TIME: - { - struct rtc_time tm; - - mutex_lock(&rtc_lock); - memset(&tm, 0, sizeof tm); - get_rtc_time(&tm); - - if (copy_to_user((struct rtc_time *) arg, &tm, - sizeof tm)) { - mutex_unlock(&rtc_lock); - return -EFAULT; - } - - mutex_unlock(&rtc_lock); - - return 0; - } - case RTC_SET_TIME: - { - int leap; - int year; - int century; - struct rtc_time tm; - - memset(&tm, 0, sizeof tm); - if (!capable(CAP_SYS_TIME)) - return -EPERM; - - if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm)) - return -EFAULT; - - /* Convert from struct tm to struct rtc_time. */ - tm.tm_year += 1900; - tm.tm_mon += 1; - - /* - * Check if tm.tm_year is a leap year. A year is a leap - * year if it is divisible by 4 but not 100, except - * that years divisible by 400 _are_ leap years. - */ - year = tm.tm_year; - leap = (tm.tm_mon == 2) && - ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0); - - /* Perform some sanity checks. */ - if ((tm.tm_year < 1970) || - (tm.tm_mon > 12) || - (tm.tm_mday == 0) || - (tm.tm_mday > days_in_month[tm.tm_mon] + leap) || - (tm.tm_wday >= 7) || - (tm.tm_hour >= 24) || - (tm.tm_min >= 60) || - (tm.tm_sec >= 60)) - return -EINVAL; - - century = (tm.tm_year >= 2000) ? 0x80 : 0; - tm.tm_year = tm.tm_year % 100; - - tm.tm_year = bin2bcd(tm.tm_year); - tm.tm_mon = bin2bcd(tm.tm_mon); - tm.tm_mday = bin2bcd(tm.tm_mday); - tm.tm_hour = bin2bcd(tm.tm_hour); - tm.tm_min = bin2bcd(tm.tm_min); - tm.tm_sec = bin2bcd(tm.tm_sec); - tm.tm_mon |= century; - - mutex_lock(&rtc_lock); - - rtc_write(RTC_YEAR, tm.tm_year); - rtc_write(RTC_MONTH, tm.tm_mon); - rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */ - rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday); - rtc_write(RTC_HOURS, tm.tm_hour); - rtc_write(RTC_MINUTES, tm.tm_min); - rtc_write(RTC_SECONDS, tm.tm_sec); - - mutex_unlock(&rtc_lock); - - return 0; - } - case RTC_VL_READ: - if (voltage_low) { - printk(KERN_ERR "%s: RTC Voltage Low - " - "reliable date/time information is no " - "longer guaranteed!\n", PCF8563_NAME); - } - - if (copy_to_user((int *) arg, &voltage_low, sizeof(int))) - return -EFAULT; - return 0; - - case RTC_VL_CLR: - { - /* Clear the VL bit in the seconds register in case - * the time has not been set already (which would - * have cleared it). This does not really matter - * because of the cached voltage_low value but do it - * anyway for consistency. */ - - int ret = rtc_read(RTC_SECONDS); - - rtc_write(RTC_SECONDS, (ret & 0x7F)); - - /* Clear the cached value. */ - voltage_low = 0; - - return 0; - } - default: - return -ENOTTY; - } - - return 0; -} - -static long pcf8563_unlocked_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - int ret; - - mutex_lock(&pcf8563_mutex); - ret = pcf8563_ioctl(filp, cmd, arg); - mutex_unlock(&pcf8563_mutex); - - return ret; -} - -static int __init pcf8563_register(void) -{ - if (pcf8563_init() < 0) { - printk(KERN_INFO "%s: Unable to initialize Real-Time Clock " - "Driver, %s\n", PCF8563_NAME, DRIVER_VERSION); - return -1; - } - - if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) { - printk(KERN_INFO "%s: Unable to get major number %d for RTC device.\n", - PCF8563_NAME, PCF8563_MAJOR); - return -1; - } - - printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, - DRIVER_VERSION); - - /* Check for low voltage, and warn about it. */ - if (voltage_low) { - printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time " - "information is no longer guaranteed!\n", PCF8563_NAME); - } - - return 0; -} - -module_init(pcf8563_register); -module_exit(pcf8563_exit); diff --git a/arch/cris/arch-v10/kernel/fasttimer.c b/arch/cris/arch-v10/kernel/fasttimer.c index 8a8196ee8ce8..082f1890bacb 100644 --- a/arch/cris/arch-v10/kernel/fasttimer.c +++ b/arch/cris/arch-v10/kernel/fasttimer.c @@ -21,8 +21,6 @@ #include #include #include -#include - #include #include diff --git a/arch/cris/arch-v10/kernel/time.c b/arch/cris/arch-v10/kernel/time.c index 20c85b5dc7d0..bcffcb6a9415 100644 --- a/arch/cris/arch-v10/kernel/time.c +++ b/arch/cris/arch-v10/kernel/time.c @@ -19,16 +19,12 @@ #include #include #include -#include #include /* define this if you need to use print_timestamp */ /* it will make jiffies at 96 hz instead of 100 hz though */ #undef USE_CASCADE_TIMERS -extern int set_rtc_mmss(unsigned long nowtime); -extern int have_rtc; - unsigned long get_ns_in_jiffie(void) { unsigned char timer_count, t1; @@ -203,11 +199,6 @@ time_init(void) */ loops_per_usec = 50; - if(RTC_INIT() < 0) - have_rtc = 0; - else - have_rtc = 1; - /* Setup the etrax timers * Base frequency is 25000 hz, divider 250 -> 100 HZ * In normal mode, we use timer0, so timer1 is free. In cascade diff --git a/arch/cris/arch-v32/kernel/time.c b/arch/cris/arch-v32/kernel/time.c index 6773fc83a670..8c4b45efd7b6 100644 --- a/arch/cris/arch-v32/kernel/time.c +++ b/arch/cris/arch-v32/kernel/time.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include @@ -67,7 +66,6 @@ unsigned long timer_regs[NR_CPUS] = }; extern int set_rtc_mmss(unsigned long nowtime); -extern int have_rtc; #ifdef CONFIG_CPU_FREQ static int @@ -265,11 +263,6 @@ void __init time_init(void) */ loops_per_usec = 50; - if(RTC_INIT() < 0) - have_rtc = 0; - else - have_rtc = 1; - /* Start CPU local timer. */ cris_timer_init(); diff --git a/arch/cris/include/asm/Kbuild b/arch/cris/include/asm/Kbuild index 956eea246b97..04d02a51c5e9 100644 --- a/arch/cris/include/asm/Kbuild +++ b/arch/cris/include/asm/Kbuild @@ -6,5 +6,4 @@ header-y += arch-v32/ header-y += ethernet.h header-y += etraxgpio.h header-y += rs485.h -header-y += rtc.h header-y += sync_serial.h diff --git a/arch/cris/include/asm/rtc.h b/arch/cris/include/asm/rtc.h deleted file mode 100644 index 17d3019529e1..000000000000 --- a/arch/cris/include/asm/rtc.h +++ /dev/null @@ -1,107 +0,0 @@ - -#ifndef __RTC_H__ -#define __RTC_H__ - -#ifdef CONFIG_ETRAX_DS1302 - /* Dallas DS1302 clock/calendar register numbers. */ -# define RTC_SECONDS 0 -# define RTC_MINUTES 1 -# define RTC_HOURS 2 -# define RTC_DAY_OF_MONTH 3 -# define RTC_MONTH 4 -# define RTC_WEEKDAY 5 -# define RTC_YEAR 6 -# define RTC_CONTROL 7 - - /* Bits in CONTROL register. */ -# define RTC_CONTROL_WRITEPROTECT 0x80 -# define RTC_TRICKLECHARGER 8 - - /* Bits in TRICKLECHARGER register TCS TCS TCS TCS DS DS RS RS. */ -# define RTC_TCR_PATTERN 0xA0 /* 1010xxxx */ -# define RTC_TCR_1DIOD 0x04 /* xxxx01xx */ -# define RTC_TCR_2DIOD 0x08 /* xxxx10xx */ -# define RTC_TCR_DISABLED 0x00 /* xxxxxx00 Disabled */ -# define RTC_TCR_2KOHM 0x01 /* xxxxxx01 2KOhm */ -# define RTC_TCR_4KOHM 0x02 /* xxxxxx10 4kOhm */ -# define RTC_TCR_8KOHM 0x03 /* xxxxxx11 8kOhm */ - -#elif defined(CONFIG_ETRAX_PCF8563) - /* I2C bus slave registers. */ -# define RTC_I2C_READ 0xa3 -# define RTC_I2C_WRITE 0xa2 - - /* Phillips PCF8563 registers. */ -# define RTC_CONTROL1 0x00 /* Control/Status register 1. */ -# define RTC_CONTROL2 0x01 /* Control/Status register 2. */ -# define RTC_CLOCKOUT_FREQ 0x0d /* CLKOUT frequency. */ -# define RTC_TIMER_CONTROL 0x0e /* Timer control. */ -# define RTC_TIMER_CNTDOWN 0x0f /* Timer countdown. */ - - /* BCD encoded clock registers. */ -# define RTC_SECONDS 0x02 -# define RTC_MINUTES 0x03 -# define RTC_HOURS 0x04 -# define RTC_DAY_OF_MONTH 0x05 -# define RTC_WEEKDAY 0x06 /* Not coded in BCD! */ -# define RTC_MONTH 0x07 -# define RTC_YEAR 0x08 -# define RTC_MINUTE_ALARM 0x09 -# define RTC_HOUR_ALARM 0x0a -# define RTC_DAY_ALARM 0x0b -# define RTC_WEEKDAY_ALARM 0x0c - -#endif - -#ifdef CONFIG_ETRAX_DS1302 -extern unsigned char ds1302_readreg(int reg); -extern void ds1302_writereg(int reg, unsigned char val); -extern int ds1302_init(void); -# define CMOS_READ(x) ds1302_readreg(x) -# define CMOS_WRITE(val,reg) ds1302_writereg(reg,val) -# define RTC_INIT() ds1302_init() -#elif defined(CONFIG_ETRAX_PCF8563) -extern unsigned char pcf8563_readreg(int reg); -extern void pcf8563_writereg(int reg, unsigned char val); -extern int pcf8563_init(void); -# define CMOS_READ(x) pcf8563_readreg(x) -# define CMOS_WRITE(val,reg) pcf8563_writereg(reg,val) -# define RTC_INIT() pcf8563_init() -#else - /* No RTC configured so we shouldn't try to access any. */ -# define CMOS_READ(x) 42 -# define CMOS_WRITE(x,y) -# define RTC_INIT() (-1) -#endif - -/* - * The struct used to pass data via the following ioctl. Similar to the - * struct tm in , but it needs to be here so that the kernel - * source is self contained, allowing cross-compiles, etc. etc. - */ -struct rtc_time { - int tm_sec; - int tm_min; - int tm_hour; - int tm_mday; - int tm_mon; - int tm_year; - int tm_wday; - int tm_yday; - int tm_isdst; -}; - -/* ioctl() calls that are permitted to the /dev/rtc interface. */ -#define RTC_MAGIC 'p' -/* Read RTC time. */ -#define RTC_RD_TIME _IOR(RTC_MAGIC, 0x09, struct rtc_time) -/* Set RTC time. */ -#define RTC_SET_TIME _IOW(RTC_MAGIC, 0x0a, struct rtc_time) -#define RTC_SET_CHARGE _IOW(RTC_MAGIC, 0x0b, int) -/* Voltage low detector */ -#define RTC_VL_READ _IOR(RTC_MAGIC, 0x13, int) -/* Clear voltage low information */ -#define RTC_VL_CLR _IO(RTC_MAGIC, 0x14) -#define RTC_MAX_IOCTL 0x14 - -#endif /* __RTC_H__ */ diff --git a/arch/cris/kernel/time.c b/arch/cris/kernel/time.c index 4e73092e85c0..277ffc459e4b 100644 --- a/arch/cris/kernel/time.c +++ b/arch/cris/kernel/time.c @@ -21,7 +21,6 @@ * */ -#include #include #include #include @@ -32,7 +31,8 @@ #include #include /* just for sched_clock() - funny that */ -int have_rtc; /* used to remember if we have an RTC or not */; + +#define D(x) #define TICK_SIZE tick @@ -50,78 +50,16 @@ u32 arch_gettimeoffset(void) } #endif -/* - * BUG: This routine does not handle hour overflow properly; it just - * sets the minutes. Usually you'll only notice that after reboot! - */ - int set_rtc_mmss(unsigned long nowtime) { - int retval = 0; - int real_seconds, real_minutes, cmos_minutes; - - printk(KERN_DEBUG "set_rtc_mmss(%lu)\n", nowtime); - - if(!have_rtc) - return 0; - - cmos_minutes = CMOS_READ(RTC_MINUTES); - cmos_minutes = bcd2bin(cmos_minutes); - - /* - * since we're only adjusting minutes and seconds, - * don't interfere with hour overflow. This avoids - * messing with unknown time zones but requires your - * RTC not to be off by more than 15 minutes - */ - real_seconds = nowtime % 60; - real_minutes = nowtime / 60; - if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) - real_minutes += 30; /* correct for half hour time zone */ - real_minutes %= 60; - - if (abs(real_minutes - cmos_minutes) < 30) { - real_seconds = bin2bcd(real_seconds); - real_minutes = bin2bcd(real_minutes); - CMOS_WRITE(real_seconds,RTC_SECONDS); - CMOS_WRITE(real_minutes,RTC_MINUTES); - } else { - printk_once(KERN_NOTICE - "set_rtc_mmss: can't update from %d to %d\n", - cmos_minutes, real_minutes); - retval = -1; - } - - return retval; + D(printk(KERN_DEBUG "set_rtc_mmss(%lu)\n", nowtime)); + return 0; } /* grab the time from the RTC chip */ - -unsigned long -get_cmos_time(void) +unsigned long get_cmos_time(void) { - unsigned int year, mon, day, hour, min, sec; - if(!have_rtc) - return 0; - - sec = CMOS_READ(RTC_SECONDS); - min = CMOS_READ(RTC_MINUTES); - hour = CMOS_READ(RTC_HOURS); - day = CMOS_READ(RTC_DAY_OF_MONTH); - mon = CMOS_READ(RTC_MONTH); - year = CMOS_READ(RTC_YEAR); - - sec = bcd2bin(sec); - min = bcd2bin(min); - hour = bcd2bin(hour); - day = bcd2bin(day); - mon = bcd2bin(mon); - year = bcd2bin(year); - - if ((year += 1900) < 1970) - year += 100; - - return mktime(year, mon, day, hour, min, sec); + return 0; } @@ -132,7 +70,7 @@ int update_persistent_clock(struct timespec now) void read_persistent_clock(struct timespec *ts) { - ts->tv_sec = get_cmos_time(); + ts->tv_sec = 0; ts->tv_nsec = 0; } -- cgit v1.2.3 From 6997991ab0db72ee92b7fd63e70679d415c8e95c Mon Sep 17 00:00:00 2001 From: Masanari Iida Date: Thu, 23 Feb 2012 23:42:19 +0900 Subject: mips: Fix printk typos in arc/mips Correct spelling "platfom" to "platform", "deactived" to "deactivated" and "deprectated" to "deprecated" in arch/mips directory. Signed-off-by: Masanari Iida Signed-off-by: Jiri Kosina --- arch/mips/kernel/mips-mt.c | 2 +- arch/mips/lantiq/xway/gpio.c | 2 +- arch/mips/lantiq/xway/gpio_ebu.c | 2 +- arch/mips/lantiq/xway/gpio_stp.c | 2 +- arch/mips/pci/pci-lantiq.c | 2 +- arch/mips/sni/pcimt.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) (limited to 'arch') diff --git a/arch/mips/kernel/mips-mt.c b/arch/mips/kernel/mips-mt.c index c23d11f6851d..b3b0b9464450 100644 --- a/arch/mips/kernel/mips-mt.c +++ b/arch/mips/kernel/mips-mt.c @@ -210,7 +210,7 @@ void mips_mt_set_cpuoptions(void) unsigned int nconfig7 = oconfig7; if (mt_opt_norps) { - printk("\"norps\" option deprectated: use \"rpsctl=\"\n"); + printk("\"norps\" option deprecated: use \"rpsctl=\"\n"); } if (mt_opt_rpsctl >= 0) { printk("34K return prediction stack override set to %d.\n", diff --git a/arch/mips/lantiq/xway/gpio.c b/arch/mips/lantiq/xway/gpio.c index d2fa98f3c78d..c429a5bc080f 100644 --- a/arch/mips/lantiq/xway/gpio.c +++ b/arch/mips/lantiq/xway/gpio.c @@ -188,7 +188,7 @@ int __init ltq_gpio_init(void) int ret = platform_driver_register(<q_gpio_driver); if (ret) - pr_info("ltq_gpio : Error registering platfom driver!"); + pr_info("ltq_gpio : Error registering platform driver!"); return ret; } diff --git a/arch/mips/lantiq/xway/gpio_ebu.c b/arch/mips/lantiq/xway/gpio_ebu.c index b91c7f17f10f..aae17170472f 100644 --- a/arch/mips/lantiq/xway/gpio_ebu.c +++ b/arch/mips/lantiq/xway/gpio_ebu.c @@ -119,7 +119,7 @@ static int __init ltq_ebu_init(void) int ret = platform_driver_register(<q_ebu_driver); if (ret) - pr_info("ltq_ebu : Error registering platfom driver!"); + pr_info("ltq_ebu : Error registering platform driver!"); return ret; } diff --git a/arch/mips/lantiq/xway/gpio_stp.c b/arch/mips/lantiq/xway/gpio_stp.c index ff9991cddeaa..fd07d87adaa9 100644 --- a/arch/mips/lantiq/xway/gpio_stp.c +++ b/arch/mips/lantiq/xway/gpio_stp.c @@ -150,7 +150,7 @@ int __init ltq_stp_init(void) int ret = platform_driver_register(<q_stp_driver); if (ret) - pr_info("ltq_stp: error registering platfom driver"); + pr_info("ltq_stp: error registering platform driver"); return ret; } diff --git a/arch/mips/pci/pci-lantiq.c b/arch/mips/pci/pci-lantiq.c index be1e1afe12c3..df3dca0ca622 100644 --- a/arch/mips/pci/pci-lantiq.c +++ b/arch/mips/pci/pci-lantiq.c @@ -296,7 +296,7 @@ int __init pcibios_init(void) { int ret = platform_driver_register(<q_pci_driver); if (ret) - printk(KERN_INFO "ltq_pci: Error registering platfom driver!"); + printk(KERN_INFO "ltq_pci: Error registering platform driver!"); return ret; } diff --git a/arch/mips/sni/pcimt.c b/arch/mips/sni/pcimt.c index ed3b3d317358..cdb1417fba59 100644 --- a/arch/mips/sni/pcimt.c +++ b/arch/mips/sni/pcimt.c @@ -29,7 +29,7 @@ static void __init sni_pcimt_sc_init(void) scsiz = cacheconf & 7; if (scsiz == 0) { - printk("Second level cache is deactived.\n"); + printk("Second level cache is deactivated.\n"); return; } if (scsiz >= 6) { -- cgit v1.2.3 From a19568ebfaf7df963be344f1052b3de2530d933c Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 28 Feb 2012 10:49:31 -0800 Subject: alpha: Use vsprintf extention %pf with builtin_return_address Emit the function name not the address when possible. builtin_return_address() gives an address. When building a kernel with CONFIG_KALLSYMS, emit the actual function name not the address. Signed-off-by: Joe Perches Signed-off-by: Jiri Kosina --- arch/alpha/kernel/pci_iommu.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'arch') diff --git a/arch/alpha/kernel/pci_iommu.c b/arch/alpha/kernel/pci_iommu.c index 43610804987d..9460b90077ca 100644 --- a/arch/alpha/kernel/pci_iommu.c +++ b/arch/alpha/kernel/pci_iommu.c @@ -236,7 +236,7 @@ static int pci_dac_dma_supported(struct pci_dev *dev, u64 mask) ok = 0; /* If both conditions above are met, we are fine. */ - DBGA("pci_dac_dma_supported %s from %p\n", + DBGA("pci_dac_dma_supported %s from %pf\n", ok ? "yes" : "no", __builtin_return_address(0)); return ok; @@ -268,7 +268,7 @@ pci_map_single_1(struct pci_dev *pdev, void *cpu_addr, size_t size, && paddr + size <= __direct_map_size) { ret = paddr + __direct_map_base; - DBGA2("pci_map_single: [%p,%zx] -> direct %llx from %p\n", + DBGA2("pci_map_single: [%p,%zx] -> direct %llx from %pf\n", cpu_addr, size, ret, __builtin_return_address(0)); return ret; @@ -279,7 +279,7 @@ pci_map_single_1(struct pci_dev *pdev, void *cpu_addr, size_t size, if (dac_allowed) { ret = paddr + alpha_mv.pci_dac_offset; - DBGA2("pci_map_single: [%p,%zx] -> DAC %llx from %p\n", + DBGA2("pci_map_single: [%p,%zx] -> DAC %llx from %pf\n", cpu_addr, size, ret, __builtin_return_address(0)); return ret; @@ -316,7 +316,7 @@ pci_map_single_1(struct pci_dev *pdev, void *cpu_addr, size_t size, ret = arena->dma_base + dma_ofs * PAGE_SIZE; ret += (unsigned long)cpu_addr & ~PAGE_MASK; - DBGA2("pci_map_single: [%p,%zx] np %ld -> sg %llx from %p\n", + DBGA2("pci_map_single: [%p,%zx] np %ld -> sg %llx from %pf\n", cpu_addr, size, npages, ret, __builtin_return_address(0)); return ret; @@ -385,14 +385,14 @@ static void alpha_pci_unmap_page(struct device *dev, dma_addr_t dma_addr, && dma_addr < __direct_map_base + __direct_map_size) { /* Nothing to do. */ - DBGA2("pci_unmap_single: direct [%llx,%zx] from %p\n", + DBGA2("pci_unmap_single: direct [%llx,%zx] from %pf\n", dma_addr, size, __builtin_return_address(0)); return; } if (dma_addr > 0xffffffff) { - DBGA2("pci64_unmap_single: DAC [%llx,%zx] from %p\n", + DBGA2("pci64_unmap_single: DAC [%llx,%zx] from %pf\n", dma_addr, size, __builtin_return_address(0)); return; } @@ -424,7 +424,7 @@ static void alpha_pci_unmap_page(struct device *dev, dma_addr_t dma_addr, spin_unlock_irqrestore(&arena->lock, flags); - DBGA2("pci_unmap_single: sg [%llx,%zx] np %ld from %p\n", + DBGA2("pci_unmap_single: sg [%llx,%zx] np %ld from %pf\n", dma_addr, size, npages, __builtin_return_address(0)); } @@ -446,7 +446,7 @@ try_again: cpu_addr = (void *)__get_free_pages(gfp, order); if (! cpu_addr) { printk(KERN_INFO "pci_alloc_consistent: " - "get_free_pages failed from %p\n", + "get_free_pages failed from %pf\n", __builtin_return_address(0)); /* ??? Really atomic allocation? Otherwise we could play with vmalloc and sg if we can't find contiguous memory. */ @@ -465,7 +465,7 @@ try_again: goto try_again; } - DBGA2("pci_alloc_consistent: %zx -> [%p,%llx] from %p\n", + DBGA2("pci_alloc_consistent: %zx -> [%p,%llx] from %pf\n", size, cpu_addr, *dma_addrp, __builtin_return_address(0)); return cpu_addr; @@ -484,7 +484,7 @@ static void alpha_pci_free_coherent(struct device *dev, size_t size, pci_unmap_single(pdev, dma_addr, size, PCI_DMA_BIDIRECTIONAL); free_pages((unsigned long)cpu_addr, get_order(size)); - DBGA2("pci_free_consistent: [%llx,%zx] from %p\n", + DBGA2("pci_free_consistent: [%llx,%zx] from %pf\n", dma_addr, size, __builtin_return_address(0)); } -- cgit v1.2.3 From 023dcd25ce409506f992f9e3a822424de035fe05 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 28 Feb 2012 10:49:32 -0800 Subject: arm: Use vsprintf extention %pf with builtin_return_address Emit the function name not the address when possible. builtin_return_address() gives an address. When building a kernel with CONFIG_KALLSYMS, emit the actual function name not the address. Signed-off-by: Joe Perches Signed-off-by: Jiri Kosina --- arch/arm/nwfpe/fpmodule.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/arm/nwfpe/fpmodule.c b/arch/arm/nwfpe/fpmodule.c index cb7658e8acc5..4e729f055a81 100644 --- a/arch/arm/nwfpe/fpmodule.c +++ b/arch/arm/nwfpe/fpmodule.c @@ -147,7 +147,7 @@ void float_raise(signed char flags) #ifdef CONFIG_DEBUG_USER if (flags & debug) printk(KERN_DEBUG - "NWFPE: %s[%d] takes exception %08x at %p from %08lx\n", + "NWFPE: %s[%d] takes exception %08x at %pf from %08lx\n", current->comm, current->pid, flags, __builtin_return_address(0), GET_USERREG()->ARM_pc); #endif -- cgit v1.2.3 From c65f2abf54a6d07260f1fa64773154e1d7047961 Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Fri, 30 Mar 2012 23:37:53 -0500 Subject: ARM: remove ixp23xx and ixp2000 platforms ixp2xxx platforms have had no real changes since ~2006 and the maintainer has said on irc that they can be removed: 13:05 < nico> do you still care about ixp2000? 13:22 < lennert> not really, no 13:58 < nico> do you think we could remove it from the kernel tree? 14:01 < lennert> go for it, and remove ixp23xx too while you're at it Removing will help simplify ARM consolidation in general and PCI re-work specifically. Signed-off-by: Rob Herring Cc: Randy Dunlap Acked-by: Lennert Buytenhek --- Documentation/arm/00-INDEX | 2 - Documentation/arm/IXP2000 | 69 --- MAINTAINERS | 17 - arch/arm/Kconfig | 26 -- arch/arm/Makefile | 2 - arch/arm/boot/compressed/head-xscale.S | 7 - arch/arm/common/Makefile | 2 - arch/arm/common/uengine.c | 507 --------------------- arch/arm/configs/ixp2000_defconfig | 99 ---- arch/arm/configs/ixp23xx_defconfig | 105 ----- arch/arm/include/asm/hardware/uengine.h | 62 --- arch/arm/mach-ixp2000/Kconfig | 72 --- arch/arm/mach-ixp2000/Makefile | 14 - arch/arm/mach-ixp2000/Makefile.boot | 3 - arch/arm/mach-ixp2000/core.c | 520 ---------------------- arch/arm/mach-ixp2000/enp2611.c | 265 ----------- arch/arm/mach-ixp2000/include/mach/debug-macro.S | 25 -- arch/arm/mach-ixp2000/include/mach/enp2611.h | 46 -- arch/arm/mach-ixp2000/include/mach/entry-macro.S | 54 --- arch/arm/mach-ixp2000/include/mach/gpio-ixp2000.h | 48 -- arch/arm/mach-ixp2000/include/mach/hardware.h | 36 -- arch/arm/mach-ixp2000/include/mach/io.h | 133 ------ arch/arm/mach-ixp2000/include/mach/irqs.h | 207 --------- arch/arm/mach-ixp2000/include/mach/ixdp2x00.h | 92 ---- arch/arm/mach-ixp2000/include/mach/ixdp2x01.h | 57 --- arch/arm/mach-ixp2000/include/mach/ixp2000-regs.h | 451 ------------------- arch/arm/mach-ixp2000/include/mach/memory.h | 31 -- arch/arm/mach-ixp2000/include/mach/platform.h | 153 ------- arch/arm/mach-ixp2000/include/mach/timex.h | 13 - arch/arm/mach-ixp2000/include/mach/uncompress.h | 47 -- arch/arm/mach-ixp2000/ixdp2400.c | 180 -------- arch/arm/mach-ixp2000/ixdp2800.c | 295 ------------ arch/arm/mach-ixp2000/ixdp2x00.c | 306 ------------- arch/arm/mach-ixp2000/ixdp2x01.c | 483 -------------------- arch/arm/mach-ixp2000/pci.c | 252 ----------- arch/arm/mach-ixp23xx/Kconfig | 25 -- arch/arm/mach-ixp23xx/Makefile | 11 - arch/arm/mach-ixp23xx/Makefile.boot | 2 - arch/arm/mach-ixp23xx/core.c | 455 ------------------- arch/arm/mach-ixp23xx/espresso.c | 93 ---- arch/arm/mach-ixp23xx/include/mach/debug-macro.S | 25 -- arch/arm/mach-ixp23xx/include/mach/entry-macro.S | 31 -- arch/arm/mach-ixp23xx/include/mach/hardware.h | 32 -- arch/arm/mach-ixp23xx/include/mach/io.h | 22 - arch/arm/mach-ixp23xx/include/mach/irqs.h | 223 ---------- arch/arm/mach-ixp23xx/include/mach/ixdp2351.h | 89 ---- arch/arm/mach-ixp23xx/include/mach/ixp23xx.h | 298 ------------- arch/arm/mach-ixp23xx/include/mach/memory.h | 34 -- arch/arm/mach-ixp23xx/include/mach/platform.h | 58 --- arch/arm/mach-ixp23xx/include/mach/time.h | 3 - arch/arm/mach-ixp23xx/include/mach/timex.h | 7 - arch/arm/mach-ixp23xx/include/mach/uncompress.h | 40 -- arch/arm/mach-ixp23xx/ixdp2351.c | 347 --------------- arch/arm/mach-ixp23xx/pci.c | 294 ------------ arch/arm/mach-ixp23xx/roadrunner.c | 180 -------- 55 files changed, 6950 deletions(-) delete mode 100644 Documentation/arm/IXP2000 delete mode 100644 arch/arm/common/uengine.c delete mode 100644 arch/arm/configs/ixp2000_defconfig delete mode 100644 arch/arm/configs/ixp23xx_defconfig delete mode 100644 arch/arm/include/asm/hardware/uengine.h delete mode 100644 arch/arm/mach-ixp2000/Kconfig delete mode 100644 arch/arm/mach-ixp2000/Makefile delete mode 100644 arch/arm/mach-ixp2000/Makefile.boot delete mode 100644 arch/arm/mach-ixp2000/core.c delete mode 100644 arch/arm/mach-ixp2000/enp2611.c delete mode 100644 arch/arm/mach-ixp2000/include/mach/debug-macro.S delete mode 100644 arch/arm/mach-ixp2000/include/mach/enp2611.h delete mode 100644 arch/arm/mach-ixp2000/include/mach/entry-macro.S delete mode 100644 arch/arm/mach-ixp2000/include/mach/gpio-ixp2000.h delete mode 100644 arch/arm/mach-ixp2000/include/mach/hardware.h delete mode 100644 arch/arm/mach-ixp2000/include/mach/io.h delete mode 100644 arch/arm/mach-ixp2000/include/mach/irqs.h delete mode 100644 arch/arm/mach-ixp2000/include/mach/ixdp2x00.h delete mode 100644 arch/arm/mach-ixp2000/include/mach/ixdp2x01.h delete mode 100644 arch/arm/mach-ixp2000/include/mach/ixp2000-regs.h delete mode 100644 arch/arm/mach-ixp2000/include/mach/memory.h delete mode 100644 arch/arm/mach-ixp2000/include/mach/platform.h delete mode 100644 arch/arm/mach-ixp2000/include/mach/timex.h delete mode 100644 arch/arm/mach-ixp2000/include/mach/uncompress.h delete mode 100644 arch/arm/mach-ixp2000/ixdp2400.c delete mode 100644 arch/arm/mach-ixp2000/ixdp2800.c delete mode 100644 arch/arm/mach-ixp2000/ixdp2x00.c delete mode 100644 arch/arm/mach-ixp2000/ixdp2x01.c delete mode 100644 arch/arm/mach-ixp2000/pci.c delete mode 100644 arch/arm/mach-ixp23xx/Kconfig delete mode 100644 arch/arm/mach-ixp23xx/Makefile delete mode 100644 arch/arm/mach-ixp23xx/Makefile.boot delete mode 100644 arch/arm/mach-ixp23xx/core.c delete mode 100644 arch/arm/mach-ixp23xx/espresso.c delete mode 100644 arch/arm/mach-ixp23xx/include/mach/debug-macro.S delete mode 100644 arch/arm/mach-ixp23xx/include/mach/entry-macro.S delete mode 100644 arch/arm/mach-ixp23xx/include/mach/hardware.h delete mode 100644 arch/arm/mach-ixp23xx/include/mach/io.h delete mode 100644 arch/arm/mach-ixp23xx/include/mach/irqs.h delete mode 100644 arch/arm/mach-ixp23xx/include/mach/ixdp2351.h delete mode 100644 arch/arm/mach-ixp23xx/include/mach/ixp23xx.h delete mode 100644 arch/arm/mach-ixp23xx/include/mach/memory.h delete mode 100644 arch/arm/mach-ixp23xx/include/mach/platform.h delete mode 100644 arch/arm/mach-ixp23xx/include/mach/time.h delete mode 100644 arch/arm/mach-ixp23xx/include/mach/timex.h delete mode 100644 arch/arm/mach-ixp23xx/include/mach/uncompress.h delete mode 100644 arch/arm/mach-ixp23xx/ixdp2351.c delete mode 100644 arch/arm/mach-ixp23xx/pci.c delete mode 100644 arch/arm/mach-ixp23xx/roadrunner.c (limited to 'arch') diff --git a/Documentation/arm/00-INDEX b/Documentation/arm/00-INDEX index 91c24a1e8a9e..36420e116c90 100644 --- a/Documentation/arm/00-INDEX +++ b/Documentation/arm/00-INDEX @@ -4,8 +4,6 @@ Booting - requirements for booting Interrupts - ARM Interrupt subsystem documentation -IXP2000 - - Release Notes for Linux on Intel's IXP2000 Network Processor msm - MSM specific documentation Netwinder diff --git a/Documentation/arm/IXP2000 b/Documentation/arm/IXP2000 deleted file mode 100644 index 68d21d92a30b..000000000000 --- a/Documentation/arm/IXP2000 +++ /dev/null @@ -1,69 +0,0 @@ - -------------------------------------------------------------------------- -Release Notes for Linux on Intel's IXP2000 Network Processor - -Maintained by Deepak Saxena -------------------------------------------------------------------------- - -1. Overview - -Intel's IXP2000 family of NPUs (IXP2400, IXP2800, IXP2850) is designed -for high-performance network applications such high-availability -telecom systems. In addition to an XScale core, it contains up to 8 -"MicroEngines" that run special code, several high-end networking -interfaces (UTOPIA, SPI, etc), a PCI host bridge, one serial port, -flash interface, and some other odds and ends. For more information, see: - -http://developer.intel.com - -2. Linux Support - -Linux currently supports the following features on the IXP2000 NPUs: - -- On-chip serial -- PCI -- Flash (MTD/JFFS2) -- I2C through GPIO -- Timers (watchdog, OS) - -That is about all we can support under Linux ATM b/c the core networking -components of the chip are accessed via Intel's closed source SDK. -Please contact Intel directly on issues with using those. There is -also a mailing list run by some folks at Princeton University that might -be of help: https://lists.cs.princeton.edu/mailman/listinfo/ixp2xxx - -WHATEVER YOU DO, DO NOT POST EMAIL TO THE LINUX-ARM OR LINUX-ARM-KERNEL -MAILING LISTS REGARDING THE INTEL SDK. - -3. Supported Platforms - -- Intel IXDP2400 Reference Platform -- Intel IXDP2800 Reference Platform -- Intel IXDP2401 Reference Platform -- Intel IXDP2801 Reference Platform -- RadiSys ENP-2611 - -4. Usage Notes - -- The IXP2000 platforms usually have rather complex PCI bus topologies - with large memory space requirements. In addition, b/c of the way the - Intel SDK is designed, devices are enumerated in a very specific - way. B/c of this this, we use "pci=firmware" option in the kernel - command line so that we do not re-enumerate the bus. - -- IXDP2x01 systems have variable clock tick rates that we cannot determine - via HW registers. The "ixdp2x01_clk=XXX" cmd line options allow you - to pass the clock rate to the board port. - -5. Thanks - -The IXP2000 work has been funded by Intel Corp. and MontaVista Software, Inc. - -The following people have contributed patches/comments/etc: - -Naeem F. Afzal -Lennert Buytenhek -Jeffrey Daly - -------------------------------------------------------------------------- -Last Update: 8/09/2004 diff --git a/MAINTAINERS b/MAINTAINERS index eecf3441ac21..defc06a0feb3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -640,13 +640,6 @@ S: Maintained F: drivers/amba/ F: include/linux/amba/bus.h -ARM/ADI ROADRUNNER MACHINE SUPPORT -M: Lennert Buytenhek -L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -S: Maintained -F: arch/arm/mach-ixp23xx/ -F: arch/arm/mach-ixp23xx/include/mach/ - ARM/ADS SPHERE MACHINE SUPPORT M: Lennert Buytenhek L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) @@ -859,21 +852,11 @@ M: Dan Williams L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -ARM/INTEL IXP2000 ARM ARCHITECTURE -M: Lennert Buytenhek -L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -S: Maintained - ARM/INTEL IXDP2850 MACHINE SUPPORT M: Lennert Buytenhek L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained -ARM/INTEL IXP23XX ARM ARCHITECTURE -M: Lennert Buytenhek -L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) -S: Maintained - ARM/INTEL IXP4XX ARM ARCHITECTURE M: Imre Kaloz M: Krzysztof Halasa diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 93180845ae16..6b242f431e32 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -527,28 +527,6 @@ config ARCH_IOP33X help Support for Intel's IOP33X (XScale) family of processors. -config ARCH_IXP23XX - bool "IXP23XX-based" - depends on MMU - select CPU_XSC3 - select PCI - select ARCH_USES_GETTIMEOFFSET - select NEED_MACH_IO_H - select NEED_MACH_MEMORY_H - help - Support for Intel's IXP23xx (XScale) family of processors. - -config ARCH_IXP2000 - bool "IXP2400/2800-based" - depends on MMU - select CPU_XSCALE - select PCI - select ARCH_USES_GETTIMEOFFSET - select NEED_MACH_IO_H - select NEED_MACH_MEMORY_H - help - Support for Intel's IXP2400/2800 (XScale) family of processors. - config ARCH_IXP4XX bool "IXP4xx-based" depends on MMU @@ -1045,10 +1023,6 @@ source "arch/arm/mach-iop13xx/Kconfig" source "arch/arm/mach-ixp4xx/Kconfig" -source "arch/arm/mach-ixp2000/Kconfig" - -source "arch/arm/mach-ixp23xx/Kconfig" - source "arch/arm/mach-kirkwood/Kconfig" source "arch/arm/mach-ks8695/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 047a20780fc1..a0c40a0986cd 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -149,8 +149,6 @@ machine-$(CONFIG_ARCH_INTEGRATOR) := integrator machine-$(CONFIG_ARCH_IOP13XX) := iop13xx machine-$(CONFIG_ARCH_IOP32X) := iop32x machine-$(CONFIG_ARCH_IOP33X) := iop33x -machine-$(CONFIG_ARCH_IXP2000) := ixp2000 -machine-$(CONFIG_ARCH_IXP23XX) := ixp23xx machine-$(CONFIG_ARCH_IXP4XX) := ixp4xx machine-$(CONFIG_ARCH_KIRKWOOD) := kirkwood machine-$(CONFIG_ARCH_KS8695) := ks8695 diff --git a/arch/arm/boot/compressed/head-xscale.S b/arch/arm/boot/compressed/head-xscale.S index aa5ee49c5c5a..6ab0599c02dd 100644 --- a/arch/arm/boot/compressed/head-xscale.S +++ b/arch/arm/boot/compressed/head-xscale.S @@ -32,10 +32,3 @@ __XScale_start: bic r0, r0, #0x1000 @ clear Icache mcr p15, 0, r0, c1, c0, 0 -#ifdef CONFIG_ARCH_IXP2000 - mov r1, #-1 - mov r0, #0xd6000000 - str r1, [r0, #0x14] - str r1, [r0, #0x18] -#endif - diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 215816f1775f..e8a4e58f1b82 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -11,7 +11,5 @@ obj-$(CONFIG_DMABOUNCE) += dmabounce.o obj-$(CONFIG_SHARP_LOCOMO) += locomo.o obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o obj-$(CONFIG_SHARP_SCOOP) += scoop.o -obj-$(CONFIG_ARCH_IXP2000) += uengine.o -obj-$(CONFIG_ARCH_IXP23XX) += uengine.o obj-$(CONFIG_PCI_HOST_ITE8152) += it8152.o obj-$(CONFIG_ARM_TIMER_SP804) += timer-sp.o diff --git a/arch/arm/common/uengine.c b/arch/arm/common/uengine.c deleted file mode 100644 index bef408f3d76c..000000000000 --- a/arch/arm/common/uengine.c +++ /dev/null @@ -1,507 +0,0 @@ -/* - * Generic library functions for the microengines found on the Intel - * IXP2000 series of network processors. - * - * Copyright (C) 2004, 2005 Lennert Buytenhek - * Dedicated to Marija Kulikova. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of the - * License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(CONFIG_ARCH_IXP2000) -#define IXP_UENGINE_CSR_VIRT_BASE IXP2000_UENGINE_CSR_VIRT_BASE -#define IXP_PRODUCT_ID IXP2000_PRODUCT_ID -#define IXP_MISC_CONTROL IXP2000_MISC_CONTROL -#define IXP_RESET1 IXP2000_RESET1 -#else -#if defined(CONFIG_ARCH_IXP23XX) -#define IXP_UENGINE_CSR_VIRT_BASE IXP23XX_UENGINE_CSR_VIRT_BASE -#define IXP_PRODUCT_ID IXP23XX_PRODUCT_ID -#define IXP_MISC_CONTROL IXP23XX_MISC_CONTROL -#define IXP_RESET1 IXP23XX_RESET1 -#else -#error unknown platform -#endif -#endif - -#define USTORE_ADDRESS 0x000 -#define USTORE_DATA_LOWER 0x004 -#define USTORE_DATA_UPPER 0x008 -#define CTX_ENABLES 0x018 -#define CC_ENABLE 0x01c -#define CSR_CTX_POINTER 0x020 -#define INDIRECT_CTX_STS 0x040 -#define ACTIVE_CTX_STS 0x044 -#define INDIRECT_CTX_SIG_EVENTS 0x048 -#define INDIRECT_CTX_WAKEUP_EVENTS 0x050 -#define NN_PUT 0x080 -#define NN_GET 0x084 -#define TIMESTAMP_LOW 0x0c0 -#define TIMESTAMP_HIGH 0x0c4 -#define T_INDEX_BYTE_INDEX 0x0f4 -#define LOCAL_CSR_STATUS 0x180 - -u32 ixp2000_uengine_mask; - -static void *ixp2000_uengine_csr_area(int uengine) -{ - return ((void *)IXP_UENGINE_CSR_VIRT_BASE) + (uengine << 10); -} - -/* - * LOCAL_CSR_STATUS=1 after a read or write to a microengine's CSR - * space means that the microengine we tried to access was also trying - * to access its own CSR space on the same clock cycle as we did. When - * this happens, we lose the arbitration process by default, and the - * read or write we tried to do was not actually performed, so we try - * again until it succeeds. - */ -u32 ixp2000_uengine_csr_read(int uengine, int offset) -{ - void *uebase; - u32 *local_csr_status; - u32 *reg; - u32 value; - - uebase = ixp2000_uengine_csr_area(uengine); - - local_csr_status = (u32 *)(uebase + LOCAL_CSR_STATUS); - reg = (u32 *)(uebase + offset); - do { - value = ixp2000_reg_read(reg); - } while (ixp2000_reg_read(local_csr_status) & 1); - - return value; -} -EXPORT_SYMBOL(ixp2000_uengine_csr_read); - -void ixp2000_uengine_csr_write(int uengine, int offset, u32 value) -{ - void *uebase; - u32 *local_csr_status; - u32 *reg; - - uebase = ixp2000_uengine_csr_area(uengine); - - local_csr_status = (u32 *)(uebase + LOCAL_CSR_STATUS); - reg = (u32 *)(uebase + offset); - do { - ixp2000_reg_write(reg, value); - } while (ixp2000_reg_read(local_csr_status) & 1); -} -EXPORT_SYMBOL(ixp2000_uengine_csr_write); - -void ixp2000_uengine_reset(u32 uengine_mask) -{ - u32 value; - - value = ixp2000_reg_read(IXP_RESET1) & ~ixp2000_uengine_mask; - - uengine_mask &= ixp2000_uengine_mask; - ixp2000_reg_wrb(IXP_RESET1, value | uengine_mask); - ixp2000_reg_wrb(IXP_RESET1, value); -} -EXPORT_SYMBOL(ixp2000_uengine_reset); - -void ixp2000_uengine_set_mode(int uengine, u32 mode) -{ - /* - * CTL_STR_PAR_EN: unconditionally enable parity checking on - * control store. - */ - mode |= 0x10000000; - ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mode); - - /* - * Enable updating of condition codes. - */ - ixp2000_uengine_csr_write(uengine, CC_ENABLE, 0x00002000); - - /* - * Initialise other per-microengine registers. - */ - ixp2000_uengine_csr_write(uengine, NN_PUT, 0x00); - ixp2000_uengine_csr_write(uengine, NN_GET, 0x00); - ixp2000_uengine_csr_write(uengine, T_INDEX_BYTE_INDEX, 0); -} -EXPORT_SYMBOL(ixp2000_uengine_set_mode); - -static int make_even_parity(u32 x) -{ - return hweight32(x) & 1; -} - -static void ustore_write(int uengine, u64 insn) -{ - /* - * Generate even parity for top and bottom 20 bits. - */ - insn |= (u64)make_even_parity((insn >> 20) & 0x000fffff) << 41; - insn |= (u64)make_even_parity(insn & 0x000fffff) << 40; - - /* - * Write to microstore. The second write auto-increments - * the USTORE_ADDRESS index register. - */ - ixp2000_uengine_csr_write(uengine, USTORE_DATA_LOWER, (u32)insn); - ixp2000_uengine_csr_write(uengine, USTORE_DATA_UPPER, (u32)(insn >> 32)); -} - -void ixp2000_uengine_load_microcode(int uengine, u8 *ucode, int insns) -{ - int i; - - /* - * Start writing to microstore at address 0. - */ - ixp2000_uengine_csr_write(uengine, USTORE_ADDRESS, 0x80000000); - for (i = 0; i < insns; i++) { - u64 insn; - - insn = (((u64)ucode[0]) << 32) | - (((u64)ucode[1]) << 24) | - (((u64)ucode[2]) << 16) | - (((u64)ucode[3]) << 8) | - ((u64)ucode[4]); - ucode += 5; - - ustore_write(uengine, insn); - } - - /* - * Pad with a few NOPs at the end (to avoid the microengine - * aborting as it prefetches beyond the last instruction), unless - * we run off the end of the instruction store first, at which - * point the address register will wrap back to zero. - */ - for (i = 0; i < 4; i++) { - u32 addr; - - addr = ixp2000_uengine_csr_read(uengine, USTORE_ADDRESS); - if (addr == 0x80000000) - break; - ustore_write(uengine, 0xf0000c0300ULL); - } - - /* - * End programming. - */ - ixp2000_uengine_csr_write(uengine, USTORE_ADDRESS, 0x00000000); -} -EXPORT_SYMBOL(ixp2000_uengine_load_microcode); - -void ixp2000_uengine_init_context(int uengine, int context, int pc) -{ - /* - * Select the right context for indirect access. - */ - ixp2000_uengine_csr_write(uengine, CSR_CTX_POINTER, context); - - /* - * Initialise signal masks to immediately go to Ready state. - */ - ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_SIG_EVENTS, 1); - ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_WAKEUP_EVENTS, 1); - - /* - * Set program counter. - */ - ixp2000_uengine_csr_write(uengine, INDIRECT_CTX_STS, pc); -} -EXPORT_SYMBOL(ixp2000_uengine_init_context); - -void ixp2000_uengine_start_contexts(int uengine, u8 ctx_mask) -{ - u32 mask; - - /* - * Enable the specified context to go to Executing state. - */ - mask = ixp2000_uengine_csr_read(uengine, CTX_ENABLES); - mask |= ctx_mask << 8; - ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mask); -} -EXPORT_SYMBOL(ixp2000_uengine_start_contexts); - -void ixp2000_uengine_stop_contexts(int uengine, u8 ctx_mask) -{ - u32 mask; - - /* - * Disable the Ready->Executing transition. Note that this - * does not stop the context until it voluntarily yields. - */ - mask = ixp2000_uengine_csr_read(uengine, CTX_ENABLES); - mask &= ~(ctx_mask << 8); - ixp2000_uengine_csr_write(uengine, CTX_ENABLES, mask); -} -EXPORT_SYMBOL(ixp2000_uengine_stop_contexts); - -static int check_ixp_type(struct ixp2000_uengine_code *c) -{ - u32 product_id; - u32 rev; - - product_id = ixp2000_reg_read(IXP_PRODUCT_ID); - if (((product_id >> 16) & 0x1f) != 0) - return 0; - - switch ((product_id >> 8) & 0xff) { -#ifdef CONFIG_ARCH_IXP2000 - case 0: /* IXP2800 */ - if (!(c->cpu_model_bitmask & 4)) - return 0; - break; - - case 1: /* IXP2850 */ - if (!(c->cpu_model_bitmask & 8)) - return 0; - break; - - case 2: /* IXP2400 */ - if (!(c->cpu_model_bitmask & 2)) - return 0; - break; -#endif - -#ifdef CONFIG_ARCH_IXP23XX - case 4: /* IXP23xx */ - if (!(c->cpu_model_bitmask & 0x3f0)) - return 0; - break; -#endif - - default: - return 0; - } - - rev = product_id & 0xff; - if (rev < c->cpu_min_revision || rev > c->cpu_max_revision) - return 0; - - return 1; -} - -static void generate_ucode(u8 *ucode, u32 *gpr_a, u32 *gpr_b) -{ - int offset; - int i; - - offset = 0; - - for (i = 0; i < 128; i++) { - u8 b3; - u8 b2; - u8 b1; - u8 b0; - - b3 = (gpr_a[i] >> 24) & 0xff; - b2 = (gpr_a[i] >> 16) & 0xff; - b1 = (gpr_a[i] >> 8) & 0xff; - b0 = gpr_a[i] & 0xff; - - /* immed[@ai, (b1 << 8) | b0] */ - /* 11110000 0000VVVV VVVV11VV VVVVVV00 1IIIIIII */ - ucode[offset++] = 0xf0; - ucode[offset++] = (b1 >> 4); - ucode[offset++] = (b1 << 4) | 0x0c | (b0 >> 6); - ucode[offset++] = (b0 << 2); - ucode[offset++] = 0x80 | i; - - /* immed_w1[@ai, (b3 << 8) | b2] */ - /* 11110100 0100VVVV VVVV11VV VVVVVV00 1IIIIIII */ - ucode[offset++] = 0xf4; - ucode[offset++] = 0x40 | (b3 >> 4); - ucode[offset++] = (b3 << 4) | 0x0c | (b2 >> 6); - ucode[offset++] = (b2 << 2); - ucode[offset++] = 0x80 | i; - } - - for (i = 0; i < 128; i++) { - u8 b3; - u8 b2; - u8 b1; - u8 b0; - - b3 = (gpr_b[i] >> 24) & 0xff; - b2 = (gpr_b[i] >> 16) & 0xff; - b1 = (gpr_b[i] >> 8) & 0xff; - b0 = gpr_b[i] & 0xff; - - /* immed[@bi, (b1 << 8) | b0] */ - /* 11110000 0000VVVV VVVV001I IIIIII11 VVVVVVVV */ - ucode[offset++] = 0xf0; - ucode[offset++] = (b1 >> 4); - ucode[offset++] = (b1 << 4) | 0x02 | (i >> 6); - ucode[offset++] = (i << 2) | 0x03; - ucode[offset++] = b0; - - /* immed_w1[@bi, (b3 << 8) | b2] */ - /* 11110100 0100VVVV VVVV001I IIIIII11 VVVVVVVV */ - ucode[offset++] = 0xf4; - ucode[offset++] = 0x40 | (b3 >> 4); - ucode[offset++] = (b3 << 4) | 0x02 | (i >> 6); - ucode[offset++] = (i << 2) | 0x03; - ucode[offset++] = b2; - } - - /* ctx_arb[kill] */ - ucode[offset++] = 0xe0; - ucode[offset++] = 0x00; - ucode[offset++] = 0x01; - ucode[offset++] = 0x00; - ucode[offset++] = 0x00; -} - -static int set_initial_registers(int uengine, struct ixp2000_uengine_code *c) -{ - int per_ctx_regs; - u32 *gpr_a; - u32 *gpr_b; - u8 *ucode; - int i; - - gpr_a = kzalloc(128 * sizeof(u32), GFP_KERNEL); - gpr_b = kzalloc(128 * sizeof(u32), GFP_KERNEL); - ucode = kmalloc(513 * 5, GFP_KERNEL); - if (gpr_a == NULL || gpr_b == NULL || ucode == NULL) { - kfree(ucode); - kfree(gpr_b); - kfree(gpr_a); - return 1; - } - - per_ctx_regs = 16; - if (c->uengine_parameters & IXP2000_UENGINE_4_CONTEXTS) - per_ctx_regs = 32; - - for (i = 0; i < 256; i++) { - struct ixp2000_reg_value *r = c->initial_reg_values + i; - u32 *bank; - int inc; - int j; - - if (r->reg == -1) - break; - - bank = (r->reg & 0x400) ? gpr_b : gpr_a; - inc = (r->reg & 0x80) ? 128 : per_ctx_regs; - - j = r->reg & 0x7f; - while (j < 128) { - bank[j] = r->value; - j += inc; - } - } - - generate_ucode(ucode, gpr_a, gpr_b); - ixp2000_uengine_load_microcode(uengine, ucode, 513); - ixp2000_uengine_init_context(uengine, 0, 0); - ixp2000_uengine_start_contexts(uengine, 0x01); - for (i = 0; i < 100; i++) { - u32 status; - - status = ixp2000_uengine_csr_read(uengine, ACTIVE_CTX_STS); - if (!(status & 0x80000000)) - break; - } - ixp2000_uengine_stop_contexts(uengine, 0x01); - - kfree(ucode); - kfree(gpr_b); - kfree(gpr_a); - - return !!(i == 100); -} - -int ixp2000_uengine_load(int uengine, struct ixp2000_uengine_code *c) -{ - int ctx; - - if (!check_ixp_type(c)) - return 1; - - if (!(ixp2000_uengine_mask & (1 << uengine))) - return 1; - - ixp2000_uengine_reset(1 << uengine); - ixp2000_uengine_set_mode(uengine, c->uengine_parameters); - if (set_initial_registers(uengine, c)) - return 1; - ixp2000_uengine_load_microcode(uengine, c->insns, c->num_insns); - - for (ctx = 0; ctx < 8; ctx++) - ixp2000_uengine_init_context(uengine, ctx, 0); - - return 0; -} -EXPORT_SYMBOL(ixp2000_uengine_load); - - -static int __init ixp2000_uengine_init(void) -{ - int uengine; - u32 value; - - /* - * Determine number of microengines present. - */ - switch ((ixp2000_reg_read(IXP_PRODUCT_ID) >> 8) & 0x1fff) { -#ifdef CONFIG_ARCH_IXP2000 - case 0: /* IXP2800 */ - case 1: /* IXP2850 */ - ixp2000_uengine_mask = 0x00ff00ff; - break; - - case 2: /* IXP2400 */ - ixp2000_uengine_mask = 0x000f000f; - break; -#endif - -#ifdef CONFIG_ARCH_IXP23XX - case 4: /* IXP23xx */ - ixp2000_uengine_mask = (*IXP23XX_EXP_CFG_FUSE >> 8) & 0xf; - break; -#endif - - default: - printk(KERN_INFO "Detected unknown IXP2000 model (%.8x)\n", - (unsigned int)ixp2000_reg_read(IXP_PRODUCT_ID)); - ixp2000_uengine_mask = 0x00000000; - break; - } - - /* - * Reset microengines. - */ - ixp2000_uengine_reset(ixp2000_uengine_mask); - - /* - * Synchronise timestamp counters across all microengines. - */ - value = ixp2000_reg_read(IXP_MISC_CONTROL); - ixp2000_reg_wrb(IXP_MISC_CONTROL, value & ~0x80); - for (uengine = 0; uengine < 32; uengine++) { - if (ixp2000_uengine_mask & (1 << uengine)) { - ixp2000_uengine_csr_write(uengine, TIMESTAMP_LOW, 0); - ixp2000_uengine_csr_write(uengine, TIMESTAMP_HIGH, 0); - } - } - ixp2000_reg_wrb(IXP_MISC_CONTROL, value | 0x80); - - return 0; -} - -subsys_initcall(ixp2000_uengine_init); diff --git a/arch/arm/configs/ixp2000_defconfig b/arch/arm/configs/ixp2000_defconfig deleted file mode 100644 index 8405aded97a3..000000000000 --- a/arch/arm/configs/ixp2000_defconfig +++ /dev/null @@ -1,99 +0,0 @@ -CONFIG_EXPERIMENTAL=y -CONFIG_SYSVIPC=y -CONFIG_BSD_PROCESS_ACCT=y -CONFIG_LOG_BUF_SHIFT=14 -CONFIG_BLK_DEV_INITRD=y -CONFIG_EXPERT=y -# CONFIG_HOTPLUG is not set -CONFIG_SLAB=y -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_ARCH_IXP2000=y -CONFIG_ARCH_ENP2611=y -CONFIG_ARCH_IXDP2400=y -CONFIG_ARCH_IXDP2800=y -CONFIG_ARCH_IXDP2401=y -CONFIG_ARCH_IXDP2801=y -# CONFIG_IXP2000_SUPPORT_BROKEN_PCI_IO is not set -# CONFIG_ARM_THUMB is not set -CONFIG_CPU_BIG_ENDIAN=y -CONFIG_ZBOOT_ROM_TEXT=0x0 -CONFIG_ZBOOT_ROM_BSS=0x0 -CONFIG_CMDLINE="console=ttyS0,57600 root=/dev/nfs ip=bootp mem=64M@0x0" -CONFIG_FPE_NWFPE=y -CONFIG_FPE_NWFPE_XP=y -CONFIG_NET=y -CONFIG_PACKET=y -CONFIG_UNIX=y -CONFIG_INET=y -CONFIG_IP_PNP=y -CONFIG_IP_PNP_DHCP=y -CONFIG_IP_PNP_BOOTP=y -CONFIG_SYN_COOKIES=y -CONFIG_IPV6=y -# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET6_XFRM_MODE_TUNNEL is not set -# CONFIG_INET6_XFRM_MODE_BEET is not set -# CONFIG_IPV6_SIT is not set -# CONFIG_PREVENT_FIRMWARE_BUILD is not set -CONFIG_MTD=y -CONFIG_MTD_PARTITIONS=y -CONFIG_MTD_REDBOOT_PARTS=y -CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y -CONFIG_MTD_REDBOOT_PARTS_READONLY=y -CONFIG_MTD_CHAR=y -CONFIG_MTD_BLOCK=y -CONFIG_MTD_CFI=y -CONFIG_MTD_CFI_INTELEXT=y -CONFIG_MTD_COMPLEX_MAPPINGS=y -CONFIG_MTD_IXP2000=y -CONFIG_BLK_DEV_LOOP=y -CONFIG_BLK_DEV_NBD=y -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_EEPROM_LEGACY=y -CONFIG_NETDEVICES=y -CONFIG_DUMMY=y -CONFIG_NET_ETHERNET=y -CONFIG_NET_PCI=y -CONFIG_CS89x0=y -CONFIG_E100=y -CONFIG_ENP2611_MSF_NET=y -CONFIG_WAN=y -CONFIG_HDLC=y -CONFIG_HDLC_RAW=y -CONFIG_HDLC_CISCO=y -CONFIG_HDLC_FR=y -CONFIG_HDLC_PPP=y -CONFIG_DLCI=y -# CONFIG_INPUT_KEYBOARD is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_SERIO is not set -# CONFIG_VT is not set -CONFIG_SERIAL_8250=y -CONFIG_SERIAL_8250_CONSOLE=y -CONFIG_SERIAL_8250_NR_UARTS=3 -# CONFIG_HW_RANDOM is not set -CONFIG_I2C=y -CONFIG_I2C_CHARDEV=y -CONFIG_I2C_IXP2000=y -CONFIG_WATCHDOG=y -CONFIG_IXP2000_WATCHDOG=y -CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_XATTR=y -CONFIG_EXT2_FS_POSIX_ACL=y -CONFIG_EXT3_FS=y -CONFIG_EXT3_FS_POSIX_ACL=y -CONFIG_INOTIFY=y -CONFIG_TMPFS=y -CONFIG_JFFS2_FS=y -CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -CONFIG_ROOT_NFS=y -CONFIG_PARTITION_ADVANCED=y -CONFIG_MAGIC_SYSRQ=y -CONFIG_DEBUG_KERNEL=y -CONFIG_DEBUG_MUTEXES=y -CONFIG_DEBUG_USER=y -CONFIG_DEBUG_ERRORS=y -CONFIG_DEBUG_LL=y diff --git a/arch/arm/configs/ixp23xx_defconfig b/arch/arm/configs/ixp23xx_defconfig deleted file mode 100644 index 688717612e91..000000000000 --- a/arch/arm/configs/ixp23xx_defconfig +++ /dev/null @@ -1,105 +0,0 @@ -CONFIG_EXPERIMENTAL=y -CONFIG_SYSVIPC=y -CONFIG_BSD_PROCESS_ACCT=y -CONFIG_LOG_BUF_SHIFT=14 -CONFIG_BLK_DEV_INITRD=y -CONFIG_EXPERT=y -CONFIG_SLAB=y -CONFIG_MODULES=y -CONFIG_MODULE_UNLOAD=y -CONFIG_ARCH_IXP23XX=y -CONFIG_MACH_ESPRESSO=y -CONFIG_MACH_IXDP2351=y -CONFIG_MACH_ROADRUNNER=y -# CONFIG_ARM_THUMB is not set -CONFIG_CPU_BIG_ENDIAN=y -CONFIG_ZBOOT_ROM_TEXT=0x0 -CONFIG_ZBOOT_ROM_BSS=0x0 -CONFIG_CMDLINE="console=ttyS0,115200 root=/dev/nfs ip=bootp" -CONFIG_FPE_NWFPE=y -CONFIG_FPE_NWFPE_XP=y -CONFIG_NET=y -CONFIG_PACKET=y -CONFIG_UNIX=y -CONFIG_INET=y -CONFIG_IP_PNP=y -CONFIG_IP_PNP_DHCP=y -CONFIG_IP_PNP_BOOTP=y -CONFIG_SYN_COOKIES=y -CONFIG_IPV6=y -# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set -# CONFIG_INET6_XFRM_MODE_TUNNEL is not set -# CONFIG_INET6_XFRM_MODE_BEET is not set -# CONFIG_IPV6_SIT is not set -# CONFIG_PREVENT_FIRMWARE_BUILD is not set -# CONFIG_FW_LOADER is not set -CONFIG_MTD=y -CONFIG_MTD_PARTITIONS=y -CONFIG_MTD_REDBOOT_PARTS=y -CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED=y -CONFIG_MTD_REDBOOT_PARTS_READONLY=y -CONFIG_MTD_CHAR=y -CONFIG_MTD_BLOCK=y -CONFIG_MTD_CFI=y -CONFIG_MTD_CFI_INTELEXT=y -CONFIG_MTD_COMPLEX_MAPPINGS=y -CONFIG_MTD_PHYSMAP=y -CONFIG_BLK_DEV_LOOP=y -CONFIG_BLK_DEV_NBD=y -CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_RAM_SIZE=8192 -CONFIG_EEPROM_LEGACY=y -CONFIG_IDE=y -CONFIG_BLK_DEV_SIIMAGE=y -CONFIG_SCSI=y -CONFIG_BLK_DEV_SD=y -CONFIG_NETDEVICES=y -CONFIG_DUMMY=y -CONFIG_NET_ETHERNET=y -CONFIG_NET_PCI=y -CONFIG_E100=y -CONFIG_E1000=y -CONFIG_WAN=y -CONFIG_HDLC=y -CONFIG_HDLC_RAW=y -CONFIG_HDLC_CISCO=y -CONFIG_HDLC_FR=y -CONFIG_HDLC_PPP=y -CONFIG_DLCI=y -# CONFIG_INPUT_KEYBOARD is not set -# CONFIG_INPUT_MOUSE is not set -# CONFIG_SERIO is not set -# CONFIG_VT is not set -CONFIG_SERIAL_8250=y -CONFIG_SERIAL_8250_CONSOLE=y -# CONFIG_HW_RANDOM is not set -CONFIG_I2C=y -CONFIG_I2C_CHARDEV=y -CONFIG_WATCHDOG=y -# CONFIG_USB_HID is not set -CONFIG_USB=y -CONFIG_USB_MON=y -CONFIG_USB_EHCI_HCD=y -CONFIG_USB_OHCI_HCD=y -CONFIG_USB_UHCI_HCD=y -CONFIG_USB_STORAGE=y -CONFIG_EXT2_FS=y -CONFIG_EXT2_FS_XATTR=y -CONFIG_EXT2_FS_POSIX_ACL=y -CONFIG_EXT3_FS=y -CONFIG_EXT3_FS_POSIX_ACL=y -CONFIG_INOTIFY=y -CONFIG_MSDOS_FS=y -CONFIG_TMPFS=y -CONFIG_JFFS2_FS=y -CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -CONFIG_ROOT_NFS=y -CONFIG_PARTITION_ADVANCED=y -CONFIG_NLS_CODEPAGE_437=y -CONFIG_MAGIC_SYSRQ=y -CONFIG_DEBUG_KERNEL=y -CONFIG_DEBUG_MUTEXES=y -CONFIG_DEBUG_USER=y -CONFIG_DEBUG_ERRORS=y -CONFIG_DEBUG_LL=y diff --git a/arch/arm/include/asm/hardware/uengine.h b/arch/arm/include/asm/hardware/uengine.h deleted file mode 100644 index b442d65c6593..000000000000 --- a/arch/arm/include/asm/hardware/uengine.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Generic library functions for the microengines found on the Intel - * IXP2000 series of network processors. - * - * Copyright (C) 2004, 2005 Lennert Buytenhek - * Dedicated to Marija Kulikova. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation; either version 2.1 of the - * License, or (at your option) any later version. - */ - -#ifndef __IXP2000_UENGINE_H -#define __IXP2000_UENGINE_H - -extern u32 ixp2000_uengine_mask; - -struct ixp2000_uengine_code -{ - u32 cpu_model_bitmask; - u8 cpu_min_revision; - u8 cpu_max_revision; - - u32 uengine_parameters; - - struct ixp2000_reg_value { - int reg; - u32 value; - } *initial_reg_values; - - int num_insns; - u8 *insns; -}; - -u32 ixp2000_uengine_csr_read(int uengine, int offset); -void ixp2000_uengine_csr_write(int uengine, int offset, u32 value); -void ixp2000_uengine_reset(u32 uengine_mask); -void ixp2000_uengine_set_mode(int uengine, u32 mode); -void ixp2000_uengine_load_microcode(int uengine, u8 *ucode, int insns); -void ixp2000_uengine_init_context(int uengine, int context, int pc); -void ixp2000_uengine_start_contexts(int uengine, u8 ctx_mask); -void ixp2000_uengine_stop_contexts(int uengine, u8 ctx_mask); -int ixp2000_uengine_load(int uengine, struct ixp2000_uengine_code *c); - -#define IXP2000_UENGINE_8_CONTEXTS 0x00000000 -#define IXP2000_UENGINE_4_CONTEXTS 0x80000000 -#define IXP2000_UENGINE_PRN_UPDATE_EVERY 0x40000000 -#define IXP2000_UENGINE_PRN_UPDATE_ON_ACCESS 0x00000000 -#define IXP2000_UENGINE_NN_FROM_SELF 0x00100000 -#define IXP2000_UENGINE_NN_FROM_PREVIOUS 0x00000000 -#define IXP2000_UENGINE_ASSERT_EMPTY_AT_3 0x000c0000 -#define IXP2000_UENGINE_ASSERT_EMPTY_AT_2 0x00080000 -#define IXP2000_UENGINE_ASSERT_EMPTY_AT_1 0x00040000 -#define IXP2000_UENGINE_ASSERT_EMPTY_AT_0 0x00000000 -#define IXP2000_UENGINE_LM_ADDR1_GLOBAL 0x00020000 -#define IXP2000_UENGINE_LM_ADDR1_PER_CONTEXT 0x00000000 -#define IXP2000_UENGINE_LM_ADDR0_GLOBAL 0x00010000 -#define IXP2000_UENGINE_LM_ADDR0_PER_CONTEXT 0x00000000 - - -#endif diff --git a/arch/arm/mach-ixp2000/Kconfig b/arch/arm/mach-ixp2000/Kconfig deleted file mode 100644 index 08d2707f6ca8..000000000000 --- a/arch/arm/mach-ixp2000/Kconfig +++ /dev/null @@ -1,72 +0,0 @@ - -if ARCH_IXP2000 - -config ARCH_SUPPORTS_BIG_ENDIAN - bool - default y - -menu "Intel IXP2400/2800 Implementation Options" - -comment "IXP2400/2800 Platforms" - -config ARCH_ENP2611 - bool "Support Radisys ENP-2611" - help - Say 'Y' here if you want your kernel to support the Radisys - ENP2611 PCI network processing card. For more information on - this card, see . - -config ARCH_IXDP2400 - bool "Support Intel IXDP2400" - help - Say 'Y' here if you want your kernel to support the Intel - IXDP2400 reference platform. For more information on - this platform, see . - -config ARCH_IXDP2800 - bool "Support Intel IXDP2800" - help - Say 'Y' here if you want your kernel to support the Intel - IXDP2800 reference platform. For more information on - this platform, see . - -config ARCH_IXDP2X00 - bool - depends on ARCH_IXDP2400 || ARCH_IXDP2800 - default y - -config ARCH_IXDP2401 - bool "Support Intel IXDP2401" - help - Say 'Y' here if you want your kernel to support the Intel - IXDP2401 reference platform. For more information on - this platform, see . - -config ARCH_IXDP2801 - bool "Support Intel IXDP2801 and IXDP28x5" - help - Say 'Y' here if you want your kernel to support the Intel - IXDP2801/2805/2855 reference platforms. For more information on - this platform, see . - -config MACH_IXDP28X5 - bool - depends on ARCH_IXDP2801 - default y - -config ARCH_IXDP2X01 - bool - depends on ARCH_IXDP2401 || ARCH_IXDP2801 - default y - -config IXP2000_SUPPORT_BROKEN_PCI_IO - bool "Support broken PCI I/O on older IXP2000s" - default y - help - Say 'N' here if you only intend to run your kernel on an - IXP2000 B0 or later model and do not need the PCI I/O - byteswap workaround. Say 'Y' otherwise. - -endmenu - -endif diff --git a/arch/arm/mach-ixp2000/Makefile b/arch/arm/mach-ixp2000/Makefile deleted file mode 100644 index 1e6139d42a92..000000000000 --- a/arch/arm/mach-ixp2000/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# -# Makefile for the linux kernel. -# -obj-y := core.o pci.o -obj-m := -obj-n := -obj- := - -obj-$(CONFIG_ARCH_ENP2611) += enp2611.o -obj-$(CONFIG_ARCH_IXDP2400) += ixdp2400.o -obj-$(CONFIG_ARCH_IXDP2800) += ixdp2800.o -obj-$(CONFIG_ARCH_IXDP2X00) += ixdp2x00.o -obj-$(CONFIG_ARCH_IXDP2X01) += ixdp2x01.o - diff --git a/arch/arm/mach-ixp2000/Makefile.boot b/arch/arm/mach-ixp2000/Makefile.boot deleted file mode 100644 index 9c7af91d93da..000000000000 --- a/arch/arm/mach-ixp2000/Makefile.boot +++ /dev/null @@ -1,3 +0,0 @@ - zreladdr-y += 0x00008000 -params_phys-y := 0x00000100 - diff --git a/arch/arm/mach-ixp2000/core.c b/arch/arm/mach-ixp2000/core.c deleted file mode 100644 index f214cdff01cb..000000000000 --- a/arch/arm/mach-ixp2000/core.c +++ /dev/null @@ -1,520 +0,0 @@ -/* - * arch/arm/mach-ixp2000/core.c - * - * Common routines used by all IXP2400/2800 based platforms. - * - * Author: Deepak Saxena - * - * Copyright 2004 (C) MontaVista Software, Inc. - * - * Based on work Copyright (C) 2002-2003 Intel Corporation - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -static DEFINE_SPINLOCK(ixp2000_slowport_lock); -static unsigned long ixp2000_slowport_irq_flags; - -/************************************************************************* - * Slowport access routines - *************************************************************************/ -void ixp2000_acquire_slowport(struct slowport_cfg *new_cfg, struct slowport_cfg *old_cfg) -{ - spin_lock_irqsave(&ixp2000_slowport_lock, ixp2000_slowport_irq_flags); - - old_cfg->CCR = *IXP2000_SLOWPORT_CCR; - old_cfg->WTC = *IXP2000_SLOWPORT_WTC2; - old_cfg->RTC = *IXP2000_SLOWPORT_RTC2; - old_cfg->PCR = *IXP2000_SLOWPORT_PCR; - old_cfg->ADC = *IXP2000_SLOWPORT_ADC; - - ixp2000_reg_write(IXP2000_SLOWPORT_CCR, new_cfg->CCR); - ixp2000_reg_write(IXP2000_SLOWPORT_WTC2, new_cfg->WTC); - ixp2000_reg_write(IXP2000_SLOWPORT_RTC2, new_cfg->RTC); - ixp2000_reg_write(IXP2000_SLOWPORT_PCR, new_cfg->PCR); - ixp2000_reg_wrb(IXP2000_SLOWPORT_ADC, new_cfg->ADC); -} - -void ixp2000_release_slowport(struct slowport_cfg *old_cfg) -{ - ixp2000_reg_write(IXP2000_SLOWPORT_CCR, old_cfg->CCR); - ixp2000_reg_write(IXP2000_SLOWPORT_WTC2, old_cfg->WTC); - ixp2000_reg_write(IXP2000_SLOWPORT_RTC2, old_cfg->RTC); - ixp2000_reg_write(IXP2000_SLOWPORT_PCR, old_cfg->PCR); - ixp2000_reg_wrb(IXP2000_SLOWPORT_ADC, old_cfg->ADC); - - spin_unlock_irqrestore(&ixp2000_slowport_lock, - ixp2000_slowport_irq_flags); -} - -/************************************************************************* - * Chip specific mappings shared by all IXP2000 systems - *************************************************************************/ -static struct map_desc ixp2000_io_desc[] __initdata = { - { - .virtual = IXP2000_CAP_VIRT_BASE, - .pfn = __phys_to_pfn(IXP2000_CAP_PHYS_BASE), - .length = IXP2000_CAP_SIZE, - .type = MT_DEVICE, - }, { - .virtual = IXP2000_INTCTL_VIRT_BASE, - .pfn = __phys_to_pfn(IXP2000_INTCTL_PHYS_BASE), - .length = IXP2000_INTCTL_SIZE, - .type = MT_DEVICE, - }, { - .virtual = IXP2000_PCI_CREG_VIRT_BASE, - .pfn = __phys_to_pfn(IXP2000_PCI_CREG_PHYS_BASE), - .length = IXP2000_PCI_CREG_SIZE, - .type = MT_DEVICE, - }, { - .virtual = IXP2000_PCI_CSR_VIRT_BASE, - .pfn = __phys_to_pfn(IXP2000_PCI_CSR_PHYS_BASE), - .length = IXP2000_PCI_CSR_SIZE, - .type = MT_DEVICE, - }, { - .virtual = IXP2000_MSF_VIRT_BASE, - .pfn = __phys_to_pfn(IXP2000_MSF_PHYS_BASE), - .length = IXP2000_MSF_SIZE, - .type = MT_DEVICE, - }, { - .virtual = IXP2000_SCRATCH_RING_VIRT_BASE, - .pfn = __phys_to_pfn(IXP2000_SCRATCH_RING_PHYS_BASE), - .length = IXP2000_SCRATCH_RING_SIZE, - .type = MT_DEVICE, - }, { - .virtual = IXP2000_SRAM0_VIRT_BASE, - .pfn = __phys_to_pfn(IXP2000_SRAM0_PHYS_BASE), - .length = IXP2000_SRAM0_SIZE, - .type = MT_DEVICE, - }, { - .virtual = IXP2000_PCI_IO_VIRT_BASE, - .pfn = __phys_to_pfn(IXP2000_PCI_IO_PHYS_BASE), - .length = IXP2000_PCI_IO_SIZE, - .type = MT_DEVICE, - }, { - .virtual = IXP2000_PCI_CFG0_VIRT_BASE, - .pfn = __phys_to_pfn(IXP2000_PCI_CFG0_PHYS_BASE), - .length = IXP2000_PCI_CFG0_SIZE, - .type = MT_DEVICE, - }, { - .virtual = IXP2000_PCI_CFG1_VIRT_BASE, - .pfn = __phys_to_pfn(IXP2000_PCI_CFG1_PHYS_BASE), - .length = IXP2000_PCI_CFG1_SIZE, - .type = MT_DEVICE, - } -}; - -void __init ixp2000_map_io(void) -{ - iotable_init(ixp2000_io_desc, ARRAY_SIZE(ixp2000_io_desc)); - - /* Set slowport to 8-bit mode. */ - ixp2000_reg_wrb(IXP2000_SLOWPORT_FRM, 1); -} - - -/************************************************************************* - * Serial port support for IXP2000 - *************************************************************************/ -static struct plat_serial8250_port ixp2000_serial_port[] = { - { - .mapbase = IXP2000_UART_PHYS_BASE, - .membase = (char *)(IXP2000_UART_VIRT_BASE + 3), - .irq = IRQ_IXP2000_UART, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, - .iotype = UPIO_MEM, - .regshift = 2, - .uartclk = 50000000, - }, - { }, -}; - -static struct resource ixp2000_uart_resource = { - .start = IXP2000_UART_PHYS_BASE, - .end = IXP2000_UART_PHYS_BASE + 0x1f, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device ixp2000_serial_device = { - .name = "serial8250", - .id = PLAT8250_DEV_PLATFORM, - .dev = { - .platform_data = ixp2000_serial_port, - }, - .num_resources = 1, - .resource = &ixp2000_uart_resource, -}; - -void __init ixp2000_uart_init(void) -{ - platform_device_register(&ixp2000_serial_device); -} - - -/************************************************************************* - * Timer-tick functions for IXP2000 - *************************************************************************/ -static unsigned ticks_per_jiffy; -static unsigned ticks_per_usec; -static unsigned next_jiffy_time; -static volatile unsigned long *missing_jiffy_timer_csr; - -unsigned long ixp2000_gettimeoffset (void) -{ - unsigned long offset; - - offset = next_jiffy_time - *missing_jiffy_timer_csr; - - return offset / ticks_per_usec; -} - -static irqreturn_t ixp2000_timer_interrupt(int irq, void *dev_id) -{ - /* clear timer 1 */ - ixp2000_reg_wrb(IXP2000_T1_CLR, 1); - - while ((signed long)(next_jiffy_time - *missing_jiffy_timer_csr) - >= ticks_per_jiffy) { - timer_tick(); - next_jiffy_time -= ticks_per_jiffy; - } - - return IRQ_HANDLED; -} - -static struct irqaction ixp2000_timer_irq = { - .name = "IXP2000 Timer Tick", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, - .handler = ixp2000_timer_interrupt, -}; - -void __init ixp2000_init_time(unsigned long tick_rate) -{ - ticks_per_jiffy = (tick_rate + HZ/2) / HZ; - ticks_per_usec = tick_rate / 1000000; - - /* - * We use timer 1 as our timer interrupt. - */ - ixp2000_reg_write(IXP2000_T1_CLR, 0); - ixp2000_reg_write(IXP2000_T1_CLD, ticks_per_jiffy - 1); - ixp2000_reg_write(IXP2000_T1_CTL, (1 << 7)); - - /* - * We use a second timer as a monotonic counter for tracking - * missed jiffies. The IXP2000 has four timers, but if we're - * on an A-step IXP2800, timer 2 and 3 don't work, so on those - * chips we use timer 4. Timer 4 is the only timer that can - * be used for the watchdog, so we use timer 2 if we're on a - * non-buggy chip. - */ - if ((*IXP2000_PRODUCT_ID & 0x001ffef0) == 0x00000000) { - printk(KERN_INFO "Enabling IXP2800 erratum #25 workaround\n"); - - ixp2000_reg_write(IXP2000_T4_CLR, 0); - ixp2000_reg_write(IXP2000_T4_CLD, -1); - ixp2000_reg_wrb(IXP2000_T4_CTL, (1 << 7)); - missing_jiffy_timer_csr = IXP2000_T4_CSR; - } else { - ixp2000_reg_write(IXP2000_T2_CLR, 0); - ixp2000_reg_write(IXP2000_T2_CLD, -1); - ixp2000_reg_wrb(IXP2000_T2_CTL, (1 << 7)); - missing_jiffy_timer_csr = IXP2000_T2_CSR; - } - next_jiffy_time = 0xffffffff; - - /* register for interrupt */ - setup_irq(IRQ_IXP2000_TIMER1, &ixp2000_timer_irq); -} - -/************************************************************************* - * GPIO helpers - *************************************************************************/ -static unsigned long GPIO_IRQ_falling_edge; -static unsigned long GPIO_IRQ_rising_edge; -static unsigned long GPIO_IRQ_level_low; -static unsigned long GPIO_IRQ_level_high; - -static void update_gpio_int_csrs(void) -{ - ixp2000_reg_write(IXP2000_GPIO_FEDR, GPIO_IRQ_falling_edge); - ixp2000_reg_write(IXP2000_GPIO_REDR, GPIO_IRQ_rising_edge); - ixp2000_reg_write(IXP2000_GPIO_LSLR, GPIO_IRQ_level_low); - ixp2000_reg_wrb(IXP2000_GPIO_LSHR, GPIO_IRQ_level_high); -} - -void gpio_line_config(int line, int direction) -{ - unsigned long flags; - - local_irq_save(flags); - if (direction == GPIO_OUT) { - /* if it's an output, it ain't an interrupt anymore */ - GPIO_IRQ_falling_edge &= ~(1 << line); - GPIO_IRQ_rising_edge &= ~(1 << line); - GPIO_IRQ_level_low &= ~(1 << line); - GPIO_IRQ_level_high &= ~(1 << line); - update_gpio_int_csrs(); - - ixp2000_reg_wrb(IXP2000_GPIO_PDSR, 1 << line); - } else if (direction == GPIO_IN) { - ixp2000_reg_wrb(IXP2000_GPIO_PDCR, 1 << line); - } - local_irq_restore(flags); -} -EXPORT_SYMBOL(gpio_line_config); - - -/************************************************************************* - * IRQ handling IXP2000 - *************************************************************************/ -static void ixp2000_GPIO_irq_handler(unsigned int irq, struct irq_desc *desc) -{ - int i; - unsigned long status = *IXP2000_GPIO_INST; - - for (i = 0; i <= 7; i++) { - if (status & (1<irq - IRQ_IXP2000_GPIO0; - - /* - * First, configure this GPIO line as an input. - */ - ixp2000_reg_write(IXP2000_GPIO_PDCR, 1 << line); - - /* - * Then, set the proper trigger type. - */ - if (type & IRQ_TYPE_EDGE_FALLING) - GPIO_IRQ_falling_edge |= 1 << line; - else - GPIO_IRQ_falling_edge &= ~(1 << line); - if (type & IRQ_TYPE_EDGE_RISING) - GPIO_IRQ_rising_edge |= 1 << line; - else - GPIO_IRQ_rising_edge &= ~(1 << line); - if (type & IRQ_TYPE_LEVEL_LOW) - GPIO_IRQ_level_low |= 1 << line; - else - GPIO_IRQ_level_low &= ~(1 << line); - if (type & IRQ_TYPE_LEVEL_HIGH) - GPIO_IRQ_level_high |= 1 << line; - else - GPIO_IRQ_level_high &= ~(1 << line); - update_gpio_int_csrs(); - - return 0; -} - -static void ixp2000_GPIO_irq_mask_ack(struct irq_data *d) -{ - unsigned int irq = d->irq; - - ixp2000_reg_write(IXP2000_GPIO_INCR, (1 << (irq - IRQ_IXP2000_GPIO0))); - - ixp2000_reg_write(IXP2000_GPIO_EDSR, (1 << (irq - IRQ_IXP2000_GPIO0))); - ixp2000_reg_write(IXP2000_GPIO_LDSR, (1 << (irq - IRQ_IXP2000_GPIO0))); - ixp2000_reg_wrb(IXP2000_GPIO_INST, (1 << (irq - IRQ_IXP2000_GPIO0))); -} - -static void ixp2000_GPIO_irq_mask(struct irq_data *d) -{ - unsigned int irq = d->irq; - - ixp2000_reg_wrb(IXP2000_GPIO_INCR, (1 << (irq - IRQ_IXP2000_GPIO0))); -} - -static void ixp2000_GPIO_irq_unmask(struct irq_data *d) -{ - unsigned int irq = d->irq; - - ixp2000_reg_write(IXP2000_GPIO_INSR, (1 << (irq - IRQ_IXP2000_GPIO0))); -} - -static struct irq_chip ixp2000_GPIO_irq_chip = { - .irq_ack = ixp2000_GPIO_irq_mask_ack, - .irq_mask = ixp2000_GPIO_irq_mask, - .irq_unmask = ixp2000_GPIO_irq_unmask, - .irq_set_type = ixp2000_GPIO_irq_type, -}; - -static void ixp2000_pci_irq_mask(struct irq_data *d) -{ - unsigned long temp = *IXP2000_PCI_XSCALE_INT_ENABLE; - if (d->irq == IRQ_IXP2000_PCIA) - ixp2000_reg_wrb(IXP2000_PCI_XSCALE_INT_ENABLE, (temp & ~(1 << 26))); - else if (d->irq == IRQ_IXP2000_PCIB) - ixp2000_reg_wrb(IXP2000_PCI_XSCALE_INT_ENABLE, (temp & ~(1 << 27))); -} - -static void ixp2000_pci_irq_unmask(struct irq_data *d) -{ - unsigned long temp = *IXP2000_PCI_XSCALE_INT_ENABLE; - if (d->irq == IRQ_IXP2000_PCIA) - ixp2000_reg_write(IXP2000_PCI_XSCALE_INT_ENABLE, (temp | (1 << 26))); - else if (d->irq == IRQ_IXP2000_PCIB) - ixp2000_reg_write(IXP2000_PCI_XSCALE_INT_ENABLE, (temp | (1 << 27))); -} - -/* - * Error interrupts. These are used extensively by the microengine drivers - */ -static void ixp2000_err_irq_handler(unsigned int irq, struct irq_desc *desc) -{ - int i; - unsigned long status = *IXP2000_IRQ_ERR_STATUS; - - for(i = 31; i >= 0; i--) { - if(status & (1 << i)) { - generic_handle_irq(IRQ_IXP2000_DRAM0_MIN_ERR + i); - } - } -} - -static void ixp2000_err_irq_mask(struct irq_data *d) -{ - ixp2000_reg_write(IXP2000_IRQ_ERR_ENABLE_CLR, - (1 << (d->irq - IRQ_IXP2000_DRAM0_MIN_ERR))); -} - -static void ixp2000_err_irq_unmask(struct irq_data *d) -{ - ixp2000_reg_write(IXP2000_IRQ_ERR_ENABLE_SET, - (1 << (d->irq - IRQ_IXP2000_DRAM0_MIN_ERR))); -} - -static struct irq_chip ixp2000_err_irq_chip = { - .irq_ack = ixp2000_err_irq_mask, - .irq_mask = ixp2000_err_irq_mask, - .irq_unmask = ixp2000_err_irq_unmask -}; - -static struct irq_chip ixp2000_pci_irq_chip = { - .irq_ack = ixp2000_pci_irq_mask, - .irq_mask = ixp2000_pci_irq_mask, - .irq_unmask = ixp2000_pci_irq_unmask -}; - -static void ixp2000_irq_mask(struct irq_data *d) -{ - ixp2000_reg_wrb(IXP2000_IRQ_ENABLE_CLR, (1 << d->irq)); -} - -static void ixp2000_irq_unmask(struct irq_data *d) -{ - ixp2000_reg_write(IXP2000_IRQ_ENABLE_SET, (1 << d->irq)); -} - -static struct irq_chip ixp2000_irq_chip = { - .irq_ack = ixp2000_irq_mask, - .irq_mask = ixp2000_irq_mask, - .irq_unmask = ixp2000_irq_unmask -}; - -void __init ixp2000_init_irq(void) -{ - int irq; - - /* - * Mask all sources - */ - ixp2000_reg_write(IXP2000_IRQ_ENABLE_CLR, 0xffffffff); - ixp2000_reg_write(IXP2000_FIQ_ENABLE_CLR, 0xffffffff); - - /* clear all GPIO edge/level detects */ - ixp2000_reg_write(IXP2000_GPIO_REDR, 0); - ixp2000_reg_write(IXP2000_GPIO_FEDR, 0); - ixp2000_reg_write(IXP2000_GPIO_LSHR, 0); - ixp2000_reg_write(IXP2000_GPIO_LSLR, 0); - ixp2000_reg_write(IXP2000_GPIO_INCR, -1); - - /* clear PCI interrupt sources */ - ixp2000_reg_wrb(IXP2000_PCI_XSCALE_INT_ENABLE, 0); - - /* - * Certain bits in the IRQ status register of the - * IXP2000 are reserved. Instead of trying to map - * things non 1:1 from bit position to IRQ number, - * we mark the reserved IRQs as invalid. This makes - * our mask/unmask code much simpler. - */ - for (irq = IRQ_IXP2000_SOFT_INT; irq <= IRQ_IXP2000_THDB3; irq++) { - if ((1 << irq) & IXP2000_VALID_IRQ_MASK) { - irq_set_chip_and_handler(irq, &ixp2000_irq_chip, - handle_level_irq); - set_irq_flags(irq, IRQF_VALID); - } else set_irq_flags(irq, 0); - } - - for (irq = IRQ_IXP2000_DRAM0_MIN_ERR; irq <= IRQ_IXP2000_SP_INT; irq++) { - if((1 << (irq - IRQ_IXP2000_DRAM0_MIN_ERR)) & - IXP2000_VALID_ERR_IRQ_MASK) { - irq_set_chip_and_handler(irq, &ixp2000_err_irq_chip, - handle_level_irq); - set_irq_flags(irq, IRQF_VALID); - } - else - set_irq_flags(irq, 0); - } - irq_set_chained_handler(IRQ_IXP2000_ERRSUM, ixp2000_err_irq_handler); - - for (irq = IRQ_IXP2000_GPIO0; irq <= IRQ_IXP2000_GPIO7; irq++) { - irq_set_chip_and_handler(irq, &ixp2000_GPIO_irq_chip, - handle_level_irq); - set_irq_flags(irq, IRQF_VALID); - } - irq_set_chained_handler(IRQ_IXP2000_GPIO, ixp2000_GPIO_irq_handler); - - /* - * Enable PCI irqs. The actual PCI[AB] decoding is done in - * entry-macro.S, so we don't need a chained handler for the - * PCI interrupt source. - */ - ixp2000_reg_write(IXP2000_IRQ_ENABLE_SET, (1 << IRQ_IXP2000_PCI)); - for (irq = IRQ_IXP2000_PCIA; irq <= IRQ_IXP2000_PCIB; irq++) { - irq_set_chip_and_handler(irq, &ixp2000_pci_irq_chip, - handle_level_irq); - set_irq_flags(irq, IRQF_VALID); - } -} - -void ixp2000_restart(char mode, const char *cmd) -{ - ixp2000_reg_wrb(IXP2000_RESET0, RSTALL); -} diff --git a/arch/arm/mach-ixp2000/enp2611.c b/arch/arm/mach-ixp2000/enp2611.c deleted file mode 100644 index 4867f408617c..000000000000 --- a/arch/arm/mach-ixp2000/enp2611.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * arch/arm/mach-ixp2000/enp2611.c - * - * Radisys ENP-2611 support. - * - * Created 2004 by Lennert Buytenhek from the ixdp2x01 code. The - * original version carries the following notices: - * - * Original Author: Andrzej Mialkowski - * Maintainer: Deepak Saxena - * - * Copyright (C) 2002-2003 Intel Corp. - * Copyright (C) 2003-2004 MontaVista Software, Inc. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/************************************************************************* - * ENP-2611 timer tick configuration - *************************************************************************/ -static void __init enp2611_timer_init(void) -{ - ixp2000_init_time(50 * 1000 * 1000); -} - -static struct sys_timer enp2611_timer = { - .init = enp2611_timer_init, - .offset = ixp2000_gettimeoffset, -}; - - -/************************************************************************* - * ENP-2611 I/O - *************************************************************************/ -static struct map_desc enp2611_io_desc[] __initdata = { - { - .virtual = ENP2611_CALEB_VIRT_BASE, - .pfn = __phys_to_pfn(ENP2611_CALEB_PHYS_BASE), - .length = ENP2611_CALEB_SIZE, - .type = MT_DEVICE, - }, { - .virtual = ENP2611_PM3386_0_VIRT_BASE, - .pfn = __phys_to_pfn(ENP2611_PM3386_0_PHYS_BASE), - .length = ENP2611_PM3386_0_SIZE, - .type = MT_DEVICE, - }, { - .virtual = ENP2611_PM3386_1_VIRT_BASE, - .pfn = __phys_to_pfn(ENP2611_PM3386_1_PHYS_BASE), - .length = ENP2611_PM3386_1_SIZE, - .type = MT_DEVICE, - } -}; - -void __init enp2611_map_io(void) -{ - ixp2000_map_io(); - iotable_init(enp2611_io_desc, ARRAY_SIZE(enp2611_io_desc)); -} - - -/************************************************************************* - * ENP-2611 PCI - *************************************************************************/ -static int enp2611_pci_setup(int nr, struct pci_sys_data *sys) -{ - sys->mem_offset = 0xe0000000; - ixp2000_pci_setup(nr, sys); - return 1; -} - -static void __init enp2611_pci_preinit(void) -{ - ixp2000_reg_write(IXP2000_PCI_ADDR_EXT, 0x00100000); - ixp2000_pci_preinit(); - pcibios_setup("firmware"); -} - -static inline int enp2611_pci_valid_device(struct pci_bus *bus, - unsigned int devfn) -{ - /* The 82559 ethernet controller appears at both PCI:1:0:0 and - * PCI:1:2:0, so let's pretend the second one isn't there. - */ - if (bus->number == 0x01 && devfn == 0x10) - return 0; - - return 1; -} - -static int enp2611_pci_read_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *value) -{ - if (enp2611_pci_valid_device(bus, devfn)) - return ixp2000_pci_read_config(bus, devfn, where, size, value); - - return PCIBIOS_DEVICE_NOT_FOUND; -} - -static int enp2611_pci_write_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 value) -{ - if (enp2611_pci_valid_device(bus, devfn)) - return ixp2000_pci_write_config(bus, devfn, where, size, value); - - return PCIBIOS_DEVICE_NOT_FOUND; -} - -static struct pci_ops enp2611_pci_ops = { - .read = enp2611_pci_read_config, - .write = enp2611_pci_write_config -}; - -static struct pci_bus * __init enp2611_pci_scan_bus(int nr, - struct pci_sys_data *sys) -{ - return pci_scan_root_bus(NULL, sys->busnr, &enp2611_pci_ops, sys, - &sys->resources); -} - -static int __init enp2611_pci_map_irq(const struct pci_dev *dev, u8 slot, - u8 pin) -{ - int irq; - - if (dev->bus->number == 0 && PCI_SLOT(dev->devfn) == 0) { - /* IXP2400. */ - irq = IRQ_IXP2000_PCIA; - } else if (dev->bus->number == 0 && PCI_SLOT(dev->devfn) == 1) { - /* 21555 non-transparent bridge. */ - irq = IRQ_IXP2000_PCIB; - } else if (dev->bus->number == 0 && PCI_SLOT(dev->devfn) == 4) { - /* PCI2050B transparent bridge. */ - irq = -1; - } else if (dev->bus->number == 1 && PCI_SLOT(dev->devfn) == 0) { - /* 82559 ethernet. */ - irq = IRQ_IXP2000_PCIA; - } else if (dev->bus->number == 1 && PCI_SLOT(dev->devfn) == 1) { - /* SPI-3 option board. */ - irq = IRQ_IXP2000_PCIB; - } else { - printk(KERN_ERR "enp2611_pci_map_irq() called for unknown " - "device PCI:%d:%d:%d\n", dev->bus->number, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - irq = -1; - } - - return irq; -} - -struct hw_pci enp2611_pci __initdata = { - .nr_controllers = 1, - .setup = enp2611_pci_setup, - .preinit = enp2611_pci_preinit, - .scan = enp2611_pci_scan_bus, - .map_irq = enp2611_pci_map_irq, -}; - -int __init enp2611_pci_init(void) -{ - if (machine_is_enp2611()) - pci_common_init(&enp2611_pci); - - return 0; -} - -subsys_initcall(enp2611_pci_init); - - -/************************************************************************* - * ENP-2611 Machine Initialization - *************************************************************************/ -static struct flash_platform_data enp2611_flash_platform_data = { - .map_name = "cfi_probe", - .width = 1, -}; - -static struct ixp2000_flash_data enp2611_flash_data = { - .platform_data = &enp2611_flash_platform_data, - .nr_banks = 1 -}; - -static struct resource enp2611_flash_resource = { - .start = 0xc4000000, - .end = 0xc4000000 + 0x00ffffff, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device enp2611_flash = { - .name = "IXP2000-Flash", - .id = 0, - .dev = { - .platform_data = &enp2611_flash_data, - }, - .num_resources = 1, - .resource = &enp2611_flash_resource, -}; - -static struct ixp2000_i2c_pins enp2611_i2c_gpio_pins = { - .sda_pin = ENP2611_GPIO_SDA, - .scl_pin = ENP2611_GPIO_SCL, -}; - -static struct platform_device enp2611_i2c_controller = { - .name = "IXP2000-I2C", - .id = 0, - .dev = { - .platform_data = &enp2611_i2c_gpio_pins - }, - .num_resources = 0 -}; - -static struct platform_device *enp2611_devices[] __initdata = { - &enp2611_flash, - &enp2611_i2c_controller -}; - -static void __init enp2611_init_machine(void) -{ - platform_add_devices(enp2611_devices, ARRAY_SIZE(enp2611_devices)); - ixp2000_uart_init(); -} - - -MACHINE_START(ENP2611, "Radisys ENP-2611 PCI network processor board") - /* Maintainer: Lennert Buytenhek */ - .atag_offset = 0x100, - .map_io = enp2611_map_io, - .init_irq = ixp2000_init_irq, - .timer = &enp2611_timer, - .init_machine = enp2611_init_machine, - .restart = ixp2000_restart, -MACHINE_END - - diff --git a/arch/arm/mach-ixp2000/include/mach/debug-macro.S b/arch/arm/mach-ixp2000/include/mach/debug-macro.S deleted file mode 100644 index bdd3ccdc2890..000000000000 --- a/arch/arm/mach-ixp2000/include/mach/debug-macro.S +++ /dev/null @@ -1,25 +0,0 @@ -/* arch/arm/mach-ixp2000/include/mach/debug-macro.S - * - * Debugging macro include header - * - * Copyright (C) 1994-1999 Russell King - * Moved from linux/arch/arm/kernel/debug.S by Ben Dooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * -*/ - - .macro addruart, rp, rv, tmp - mov \rp, #0x00030000 -#ifdef __ARMEB__ - orr \rp, \rp, #0x00000003 -#endif - orr \rv, \rp, #0xfe000000 @ virtual base - orr \rv, \rv, #0x00f00000 - orr \rp, \rp, #0xc0000000 @ Physical base - .endm - -#define UART_SHIFT 2 -#include diff --git a/arch/arm/mach-ixp2000/include/mach/enp2611.h b/arch/arm/mach-ixp2000/include/mach/enp2611.h deleted file mode 100644 index 9ce3690061d5..000000000000 --- a/arch/arm/mach-ixp2000/include/mach/enp2611.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * arch/arm/mach-ixp2000/include/mach/enp2611.h - * - * Register and other defines for Radisys ENP-2611 - * - * Created 2004 by Lennert Buytenhek from the ixdp2x01 code. The - * original version carries the following notices: - * - * Original Author: Naeem Afzal - * Maintainer: Deepak Saxena - * - * Copyright (C) 2002 Intel Corp. - * Copyright (C) 2003-2004 MontaVista Software, Inc. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef __ENP2611_H -#define __ENP2611_H - -#define ENP2611_CALEB_PHYS_BASE 0xc5000000 -#define ENP2611_CALEB_VIRT_BASE 0xfe000000 -#define ENP2611_CALEB_SIZE 0x00100000 - -#define ENP2611_PM3386_0_PHYS_BASE 0xc6000000 -#define ENP2611_PM3386_0_VIRT_BASE 0xfe100000 -#define ENP2611_PM3386_0_SIZE 0x00100000 - -#define ENP2611_PM3386_1_PHYS_BASE 0xc6400000 -#define ENP2611_PM3386_1_VIRT_BASE 0xfe200000 -#define ENP2611_PM3386_1_SIZE 0x00100000 - -#define ENP2611_GPIO_SCL 7 -#define ENP2611_GPIO_SDA 6 - -#define IRQ_ENP2611_THERMAL IRQ_IXP2000_GPIO4 -#define IRQ_ENP2611_OPTION_BOARD IRQ_IXP2000_GPIO3 -#define IRQ_ENP2611_CALEB IRQ_IXP2000_GPIO2 -#define IRQ_ENP2611_PM3386_1 IRQ_IXP2000_GPIO1 -#define IRQ_ENP2611_PM3386_0 IRQ_IXP2000_GPIO0 - - -#endif diff --git a/arch/arm/mach-ixp2000/include/mach/entry-macro.S b/arch/arm/mach-ixp2000/include/mach/entry-macro.S deleted file mode 100644 index c4444dff9202..000000000000 --- a/arch/arm/mach-ixp2000/include/mach/entry-macro.S +++ /dev/null @@ -1,54 +0,0 @@ -/* - * arch/arm/mach-ixp2000/include/mach/entry-macro.S - * - * Low-level IRQ helper macros for IXP2000-based platforms - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ -#include - - .macro get_irqnr_preamble, base, tmp - .endm - - .macro get_irqnr_and_base, irqnr, irqstat, base, tmp - - mov \irqnr, #0x0 @clear out irqnr as default - mov \base, #0xfe000000 - orr \base, \base, #0x00e00000 - orr \base, \base, #0x08 - ldr \irqstat, [\base] @ get interrupts - - cmp \irqstat, #0 - beq 1001f - - clz \irqnr, \irqstat - mov \base, #31 - subs \irqnr, \base, \irqnr - - /* - * We handle PCIA and PCIB here so we don't have an - * extra layer of code just to check these two bits. - */ - cmp \irqnr, #IRQ_IXP2000_PCI - bne 1001f - - mov \base, #0xfe000000 - orr \base, \base, #0x00c00000 - orr \base, \base, #0x00000100 - orr \base, \base, #0x00000058 - ldr \irqstat, [\base] - - mov \tmp, #(1<<26) - tst \irqstat, \tmp - movne \irqnr, #IRQ_IXP2000_PCIA - bne 1001f - - mov \tmp, #(1<<27) - tst \irqstat, \tmp - movne \irqnr, #IRQ_IXP2000_PCIB - -1001: - .endm - diff --git a/arch/arm/mach-ixp2000/include/mach/gpio-ixp2000.h b/arch/arm/mach-ixp2000/include/mach/gpio-ixp2000.h deleted file mode 100644 index af836c76c3f1..000000000000 --- a/arch/arm/mach-ixp2000/include/mach/gpio-ixp2000.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * arch/arm/mach-ixp2000/include/mach/gpio.h - * - * Copyright (C) 2002 Intel Corporation. - * - * This program is free software, you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* - * IXP2000 GPIO in/out, edge/level detection for IRQs: - * IRQs are generated on Falling-edge, Rising-Edge, Level-low, Level-High - * or both Falling-edge and Rising-edge. - * This must be called *before* the corresponding IRQ is registerd. - * Use this instead of directly setting the GPIO registers. - * GPIOs may also be used as GPIOs (e.g. for emulating i2c/smb) - */ -#ifndef __ASM_ARCH_GPIO_H -#define __ASM_ARCH_GPIO_H - -#ifndef __ASSEMBLY__ - -#define GPIO_IN 0 -#define GPIO_OUT 1 - -#define IXP2000_GPIO_LOW 0 -#define IXP2000_GPIO_HIGH 1 - -extern void gpio_line_config(int line, int direction); - -static inline int gpio_line_get(int line) -{ - return (((*IXP2000_GPIO_PLR) >> line) & 1); -} - -static inline void gpio_line_set(int line, int value) -{ - if (value == IXP2000_GPIO_HIGH) { - ixp2000_reg_write(IXP2000_GPIO_POSR, 1 << line); - } else if (value == IXP2000_GPIO_LOW) { - ixp2000_reg_write(IXP2000_GPIO_POCR, 1 << line); - } -} - -#endif /* !__ASSEMBLY__ */ - -#endif /* ASM_ARCH_IXP2000_GPIO_H_ */ diff --git a/arch/arm/mach-ixp2000/include/mach/hardware.h b/arch/arm/mach-ixp2000/include/mach/hardware.h deleted file mode 100644 index cdaf1db84003..000000000000 --- a/arch/arm/mach-ixp2000/include/mach/hardware.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * arch/arm/mach-ixp2000/include/mach/hardware.h - * - * Hardware definitions for IXP2400/2800 based systems - * - * Original Author: Naeem M Afzal - * - * Maintainer: Deepak Saxena - * - * Copyright (C) 2001-2002 Intel Corp. - * Copyright (C) 2003-2004 MontaVista Software, Inc. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef __ASM_ARCH_HARDWARE_H__ -#define __ASM_ARCH_HARDWARE_H__ - -#include "ixp2000-regs.h" /* Chipset Registers */ - -/* - * Platform helper functions - */ -#include "platform.h" - -/* - * Platform-specific bits - */ -#include "enp2611.h" /* ENP-2611 */ -#include "ixdp2x00.h" /* IXDP2400/2800 */ -#include "ixdp2x01.h" /* IXDP2401/2801 */ - -#endif /* _ASM_ARCH_HARDWARE_H__ */ diff --git a/arch/arm/mach-ixp2000/include/mach/io.h b/arch/arm/mach-ixp2000/include/mach/io.h deleted file mode 100644 index f6552d6f35ab..000000000000 --- a/arch/arm/mach-ixp2000/include/mach/io.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - * arch/arm/mach-ixp2000/include/mach/io.h - * - * Original Author: Naeem M Afzal - * Maintainer: Deepak Saxena - * - * Copyright (C) 2002 Intel Corp. - * Copyrgiht (C) 2003-2004 MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __ASM_ARM_ARCH_IO_H -#define __ASM_ARM_ARCH_IO_H - -#include - -#define IO_SPACE_LIMIT 0xffffffff - -/* - * The A? revisions of the IXP2000s assert byte lanes for PCI I/O - * transactions the other way round (MEM transactions don't have this - * issue), so if we want to support those models, we need to override - * the standard I/O functions. - * - * B0 and later have a bit that can be set to 1 to get the proper - * behavior for I/O transactions, which then allows us to use the - * standard I/O functions. This is what we do if the user does not - * explicitly ask for support for pre-B0. - */ -#ifdef CONFIG_IXP2000_SUPPORT_BROKEN_PCI_IO -#define ___io(p) ((void __iomem *)((p)+IXP2000_PCI_IO_VIRT_BASE)) - -#define alignb(addr) (void __iomem *)((unsigned long)(addr) ^ 3) -#define alignw(addr) (void __iomem *)((unsigned long)(addr) ^ 2) - -#define outb(v,p) __raw_writeb((v),alignb(___io(p))) -#define outw(v,p) __raw_writew((v),alignw(___io(p))) -#define outl(v,p) __raw_writel((v),___io(p)) - -#define inb(p) ({ unsigned int __v = __raw_readb(alignb(___io(p))); __v; }) -#define inw(p) \ - ({ unsigned int __v = (__raw_readw(alignw(___io(p)))); __v; }) -#define inl(p) \ - ({ unsigned int __v = (__raw_readl(___io(p))); __v; }) - -#define outsb(p,d,l) __raw_writesb(alignb(___io(p)),d,l) -#define outsw(p,d,l) __raw_writesw(alignw(___io(p)),d,l) -#define outsl(p,d,l) __raw_writesl(___io(p),d,l) - -#define insb(p,d,l) __raw_readsb(alignb(___io(p)),d,l) -#define insw(p,d,l) __raw_readsw(alignw(___io(p)),d,l) -#define insl(p,d,l) __raw_readsl(___io(p),d,l) - -#define __is_io_address(p) ((((unsigned long)(p)) & ~(IXP2000_PCI_IO_SIZE - 1)) == IXP2000_PCI_IO_VIRT_BASE) - -#define ioread8(p) \ - ({ \ - unsigned int __v; \ - \ - if (__is_io_address(p)) { \ - __v = __raw_readb(alignb(p)); \ - } else { \ - __v = __raw_readb(p); \ - } \ - \ - __v; \ - }) \ - -#define ioread16(p) \ - ({ \ - unsigned int __v; \ - \ - if (__is_io_address(p)) { \ - __v = __raw_readw(alignw(p)); \ - } else { \ - __v = le16_to_cpu(__raw_readw(p)); \ - } \ - \ - __v; \ - }) - -#define ioread32(p) \ - ({ \ - unsigned int __v; \ - \ - if (__is_io_address(p)) { \ - __v = __raw_readl(p); \ - } else { \ - __v = le32_to_cpu(__raw_readl(p)); \ - } \ - \ - __v; \ - }) - -#define iowrite8(v,p) \ - ({ \ - if (__is_io_address(p)) { \ - __raw_writeb((v), alignb(p)); \ - } else { \ - __raw_writeb((v), p); \ - } \ - }) - -#define iowrite16(v,p) \ - ({ \ - if (__is_io_address(p)) { \ - __raw_writew((v), alignw(p)); \ - } else { \ - __raw_writew(cpu_to_le16(v), p); \ - } \ - }) - -#define iowrite32(v,p) \ - ({ \ - if (__is_io_address(p)) { \ - __raw_writel((v), p); \ - } else { \ - __raw_writel(cpu_to_le32(v), p); \ - } \ - }) - -#define ioport_map(port, nr) ___io(port) - -#define ioport_unmap(addr) -#else -#define __io(p) ((void __iomem *)((p)+IXP2000_PCI_IO_VIRT_BASE)) -#endif - - -#endif diff --git a/arch/arm/mach-ixp2000/include/mach/irqs.h b/arch/arm/mach-ixp2000/include/mach/irqs.h deleted file mode 100644 index bee96bcafdca..000000000000 --- a/arch/arm/mach-ixp2000/include/mach/irqs.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - * arch/arm/mach-ixp2000/include/mach/irqs.h - * - * Original Author: Naeem Afzal - * Maintainer: Deepak Saxena - * - * Copyright (C) 2002 Intel Corp. - * Copyright (C) 2003-2004 MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef _IRQS_H -#define _IRQS_H - -/* - * Do NOT add #ifdef MACHINE_FOO in here. - * Simpy add your machine IRQs here and increase NR_IRQS if needed to - * hold your machine's IRQ table. - */ - -/* - * Some interrupt numbers go unused b/c the IRQ mask/ummask/status - * register has those bit reserved. We just mark those interrupts - * as invalid and this allows us to do mask/unmask with a single - * shift operation instead of having to map the IRQ number to - * a HW IRQ number. - */ -#define IRQ_IXP2000_SOFT_INT 0 /* soft interrupt */ -#define IRQ_IXP2000_ERRSUM 1 /* OR of all bits in ErrorStatus reg*/ -#define IRQ_IXP2000_UART 2 -#define IRQ_IXP2000_GPIO 3 -#define IRQ_IXP2000_TIMER1 4 -#define IRQ_IXP2000_TIMER2 5 -#define IRQ_IXP2000_TIMER3 6 -#define IRQ_IXP2000_TIMER4 7 -#define IRQ_IXP2000_PMU 8 -#define IRQ_IXP2000_SPF 9 /* Slow port framer IRQ */ -#define IRQ_IXP2000_DMA1 10 -#define IRQ_IXP2000_DMA2 11 -#define IRQ_IXP2000_DMA3 12 -#define IRQ_IXP2000_PCI_DOORBELL 13 -#define IRQ_IXP2000_ME_ATTN 14 -#define IRQ_IXP2000_PCI 15 /* PCI INTA or INTB */ -#define IRQ_IXP2000_THDA0 16 /* thread 0-31A */ -#define IRQ_IXP2000_THDA1 17 /* thread 32-63A, IXP2800 only */ -#define IRQ_IXP2000_THDA2 18 /* thread 64-95A */ -#define IRQ_IXP2000_THDA3 19 /* thread 96-127A, IXP2800 only */ -#define IRQ_IXP2000_THDB0 24 /* thread 0-31B */ -#define IRQ_IXP2000_THDB1 25 /* thread 32-63B, IXP2800 only */ -#define IRQ_IXP2000_THDB2 26 /* thread 64-95B */ -#define IRQ_IXP2000_THDB3 27 /* thread 96-127B, IXP2800 only */ - -/* define generic GPIOs */ -#define IRQ_IXP2000_GPIO0 32 -#define IRQ_IXP2000_GPIO1 33 -#define IRQ_IXP2000_GPIO2 34 -#define IRQ_IXP2000_GPIO3 35 -#define IRQ_IXP2000_GPIO4 36 -#define IRQ_IXP2000_GPIO5 37 -#define IRQ_IXP2000_GPIO6 38 -#define IRQ_IXP2000_GPIO7 39 - -/* split off the 2 PCI sources */ -#define IRQ_IXP2000_PCIA 40 -#define IRQ_IXP2000_PCIB 41 - -/* Int sources from IRQ_ERROR_STATUS */ -#define IRQ_IXP2000_DRAM0_MIN_ERR 42 -#define IRQ_IXP2000_DRAM0_MAJ_ERR 43 -#define IRQ_IXP2000_DRAM1_MIN_ERR 44 -#define IRQ_IXP2000_DRAM1_MAJ_ERR 45 -#define IRQ_IXP2000_DRAM2_MIN_ERR 46 -#define IRQ_IXP2000_DRAM2_MAJ_ERR 47 -/* 48-57 reserved */ -#define IRQ_IXP2000_SRAM0_ERR 58 -#define IRQ_IXP2000_SRAM1_ERR 59 -#define IRQ_IXP2000_SRAM2_ERR 60 -#define IRQ_IXP2000_SRAM3_ERR 61 -/* 62-65 reserved */ -#define IRQ_IXP2000_MEDIA_ERR 66 -#define IRQ_IXP2000_PCI_ERR 67 -#define IRQ_IXP2000_SP_INT 68 - -#define NR_IXP2000_IRQS 69 - -#define IXP2000_BOARD_IRQ(x) (NR_IXP2000_IRQS + (x)) - -#define IXP2000_BOARD_IRQ_MASK(irq) (1 << (irq - NR_IXP2000_IRQS)) - -#define IXP2000_ERR_IRQ_MASK(irq) ( 1 << (irq - IRQ_IXP2000_DRAM0_MIN_ERR)) -#define IXP2000_VALID_ERR_IRQ_MASK (\ - IXP2000_ERR_IRQ_MASK(IRQ_IXP2000_DRAM0_MIN_ERR) | \ - IXP2000_ERR_IRQ_MASK(IRQ_IXP2000_DRAM0_MAJ_ERR) | \ - IXP2000_ERR_IRQ_MASK(IRQ_IXP2000_DRAM1_MIN_ERR) | \ - IXP2000_ERR_IRQ_MASK(IRQ_IXP2000_DRAM1_MAJ_ERR) | \ - IXP2000_ERR_IRQ_MASK(IRQ_IXP2000_DRAM2_MIN_ERR) | \ - IXP2000_ERR_IRQ_MASK(IRQ_IXP2000_DRAM2_MAJ_ERR) | \ - IXP2000_ERR_IRQ_MASK(IRQ_IXP2000_SRAM0_ERR) | \ - IXP2000_ERR_IRQ_MASK(IRQ_IXP2000_SRAM1_ERR) | \ - IXP2000_ERR_IRQ_MASK(IRQ_IXP2000_SRAM2_ERR) | \ - IXP2000_ERR_IRQ_MASK(IRQ_IXP2000_SRAM3_ERR) | \ - IXP2000_ERR_IRQ_MASK(IRQ_IXP2000_MEDIA_ERR) | \ - IXP2000_ERR_IRQ_MASK(IRQ_IXP2000_PCI_ERR) | \ - IXP2000_ERR_IRQ_MASK(IRQ_IXP2000_SP_INT) ) - -/* - * This allows for all the on-chip sources plus up to 32 CPLD based - * IRQs. Should be more than enough. - */ -#define IXP2000_BOARD_IRQS 32 -#define NR_IRQS (NR_IXP2000_IRQS + IXP2000_BOARD_IRQS) - - -/* - * IXDP2400 specific IRQs - */ -#define IRQ_IXDP2400_INGRESS_NPU IXP2000_BOARD_IRQ(0) -#define IRQ_IXDP2400_ENET IXP2000_BOARD_IRQ(1) -#define IRQ_IXDP2400_MEDIA_PCI IXP2000_BOARD_IRQ(2) -#define IRQ_IXDP2400_MEDIA_SP IXP2000_BOARD_IRQ(3) -#define IRQ_IXDP2400_SF_PCI IXP2000_BOARD_IRQ(4) -#define IRQ_IXDP2400_SF_SP IXP2000_BOARD_IRQ(5) -#define IRQ_IXDP2400_PMC IXP2000_BOARD_IRQ(6) -#define IRQ_IXDP2400_TVM IXP2000_BOARD_IRQ(7) - -#define NR_IXDP2400_IRQS ((IRQ_IXDP2400_TVM)+1) -#define IXDP2400_NR_IRQS NR_IXDP2400_IRQS - NR_IXP2000_IRQS - -/* IXDP2800 specific IRQs */ -#define IRQ_IXDP2800_EGRESS_ENET IXP2000_BOARD_IRQ(0) -#define IRQ_IXDP2800_INGRESS_NPU IXP2000_BOARD_IRQ(1) -#define IRQ_IXDP2800_PMC IXP2000_BOARD_IRQ(2) -#define IRQ_IXDP2800_FABRIC_PCI IXP2000_BOARD_IRQ(3) -#define IRQ_IXDP2800_FABRIC IXP2000_BOARD_IRQ(4) -#define IRQ_IXDP2800_MEDIA IXP2000_BOARD_IRQ(5) - -#define NR_IXDP2800_IRQS ((IRQ_IXDP2800_MEDIA)+1) -#define IXDP2800_NR_IRQS NR_IXDP2800_IRQS - NR_IXP2000_IRQS - -/* - * IRQs on both IXDP2x01 boards - */ -#define IRQ_IXDP2X01_SPCI_DB_0 IXP2000_BOARD_IRQ(2) -#define IRQ_IXDP2X01_SPCI_DB_1 IXP2000_BOARD_IRQ(3) -#define IRQ_IXDP2X01_SPCI_PMC_INTA IXP2000_BOARD_IRQ(4) -#define IRQ_IXDP2X01_SPCI_PMC_INTB IXP2000_BOARD_IRQ(5) -#define IRQ_IXDP2X01_SPCI_PMC_INTC IXP2000_BOARD_IRQ(6) -#define IRQ_IXDP2X01_SPCI_PMC_INTD IXP2000_BOARD_IRQ(7) -#define IRQ_IXDP2X01_SPCI_FIC_INT IXP2000_BOARD_IRQ(8) -#define IRQ_IXDP2X01_IPMI_FROM IXP2000_BOARD_IRQ(16) -#define IRQ_IXDP2X01_125US IXP2000_BOARD_IRQ(17) -#define IRQ_IXDP2X01_DB_0_ADD IXP2000_BOARD_IRQ(18) -#define IRQ_IXDP2X01_DB_1_ADD IXP2000_BOARD_IRQ(19) -#define IRQ_IXDP2X01_UART1 IXP2000_BOARD_IRQ(21) -#define IRQ_IXDP2X01_UART2 IXP2000_BOARD_IRQ(22) -#define IRQ_IXDP2X01_FIC_ADD_INT IXP2000_BOARD_IRQ(24) -#define IRQ_IXDP2X01_CS8900 IXP2000_BOARD_IRQ(25) -#define IRQ_IXDP2X01_BBSRAM IXP2000_BOARD_IRQ(26) - -#define IXDP2X01_VALID_IRQ_MASK ( \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_SPCI_DB_0) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_SPCI_DB_1) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_SPCI_PMC_INTA) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_SPCI_PMC_INTB) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_SPCI_PMC_INTC) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_SPCI_PMC_INTD) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_SPCI_FIC_INT) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_IPMI_FROM) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_125US) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_DB_0_ADD) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_DB_1_ADD) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_UART1) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_UART2) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_FIC_ADD_INT) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_CS8900) | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2X01_BBSRAM) ) - -/* - * IXDP2401 specific IRQs - */ -#define IRQ_IXDP2401_INTA_82546 IXP2000_BOARD_IRQ(0) -#define IRQ_IXDP2401_INTB_82546 IXP2000_BOARD_IRQ(1) - -#define IXDP2401_VALID_IRQ_MASK ( \ - IXDP2X01_VALID_IRQ_MASK | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2401_INTA_82546) |\ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2401_INTB_82546)) - -/* - * IXDP2801-specific IRQs - */ -#define IRQ_IXDP2801_RIV IXP2000_BOARD_IRQ(0) -#define IRQ_IXDP2801_CNFG_MEDIA IXP2000_BOARD_IRQ(27) -#define IRQ_IXDP2801_CLOCK_REF IXP2000_BOARD_IRQ(28) - -#define IXDP2801_VALID_IRQ_MASK ( \ - IXDP2X01_VALID_IRQ_MASK | \ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2801_RIV) |\ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2801_CNFG_MEDIA) |\ - IXP2000_BOARD_IRQ_MASK(IRQ_IXDP2801_CLOCK_REF)) - -#define NR_IXDP2X01_IRQS ((IRQ_IXDP2801_CLOCK_REF) + 1) - -#endif /*_IRQS_H*/ diff --git a/arch/arm/mach-ixp2000/include/mach/ixdp2x00.h b/arch/arm/mach-ixp2000/include/mach/ixdp2x00.h deleted file mode 100644 index 5df8479d9481..000000000000 --- a/arch/arm/mach-ixp2000/include/mach/ixdp2x00.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * arch/arm/mach-ixp2000/include/mach/ixdp2x00.h - * - * Register and other defines for IXDP2[48]00 platforms - * - * Original Author: Naeem Afzal - * Maintainer: Deepak Saxena - * - * Copyright (C) 2002 Intel Corp. - * Copyright (C) 2003-2004 MontaVista Software, Inc. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ -#ifndef _IXDP2X00_H_ -#define _IXDP2X00_H_ - -/* - * On board CPLD memory map - */ -#define IXDP2X00_PHYS_CPLD_BASE 0xc7000000 -#define IXDP2X00_VIRT_CPLD_BASE 0xfe000000 -#define IXDP2X00_CPLD_SIZE 0x00100000 - - -#define IXDP2X00_CPLD_REG(x) \ - (volatile unsigned long *)(IXDP2X00_VIRT_CPLD_BASE | x) - -/* - * IXDP2400 CPLD registers - */ -#define IXDP2400_CPLD_SYSLED IXDP2X00_CPLD_REG(0x0) -#define IXDP2400_CPLD_DISP_DATA IXDP2X00_CPLD_REG(0x4) -#define IXDP2400_CPLD_CLOCK_SPEED IXDP2X00_CPLD_REG(0x8) -#define IXDP2400_CPLD_INT_STAT IXDP2X00_CPLD_REG(0xc) -#define IXDP2400_CPLD_REV IXDP2X00_CPLD_REG(0x10) -#define IXDP2400_CPLD_SYS_CLK_M IXDP2X00_CPLD_REG(0x14) -#define IXDP2400_CPLD_SYS_CLK_N IXDP2X00_CPLD_REG(0x18) -#define IXDP2400_CPLD_INT_MASK IXDP2X00_CPLD_REG(0x48) - -/* - * IXDP2800 CPLD registers - */ -#define IXDP2800_CPLD_INT_STAT IXDP2X00_CPLD_REG(0x0) -#define IXDP2800_CPLD_INT_MASK IXDP2X00_CPLD_REG(0x140) - - -#define IXDP2X00_GPIO_I2C_ENABLE 0x02 -#define IXDP2X00_GPIO_SCL 0x07 -#define IXDP2X00_GPIO_SDA 0x06 - -/* - * PCI devfns for on-board devices. We need these to be able to - * properly translate IRQs and for device removal. - */ -#define IXDP2400_SLAVE_ENET_DEVFN 0x18 /* Bus 1 */ -#define IXDP2400_MASTER_ENET_DEVFN 0x20 /* Bus 1 */ -#define IXDP2400_MEDIA_DEVFN 0x28 /* Bus 1 */ -#define IXDP2400_SWITCH_FABRIC_DEVFN 0x30 /* Bus 1 */ - -#define IXDP2800_SLAVE_ENET_DEVFN 0x20 /* Bus 1 */ -#define IXDP2800_MASTER_ENET_DEVFN 0x18 /* Bus 1 */ -#define IXDP2800_SWITCH_FABRIC_DEVFN 0x30 /* Bus 1 */ - -#define IXDP2X00_P2P_DEVFN 0x20 /* Bus 0 */ -#define IXDP2X00_21555_DEVFN 0x30 /* Bus 0 */ -#define IXDP2X00_SLAVE_NPU_DEVFN 0x28 /* Bus 1 */ -#define IXDP2X00_PMC_DEVFN 0x38 /* Bus 1 */ -#define IXDP2X00_MASTER_NPU_DEVFN 0x38 /* Bus 1 */ - -#ifndef __ASSEMBLY__ -/* - * The master NPU is always PCI master. - */ -static inline unsigned int ixdp2x00_master_npu(void) -{ - return !!ixp2000_is_pcimaster(); -} - -/* - * Helper functions used by ixdp2400 and ixdp2800 specific code - */ -void ixdp2x00_init_irq(volatile unsigned long*, volatile unsigned long *, unsigned long); -void ixdp2x00_slave_pci_postinit(void); -void ixdp2x00_init_machine(void); -void ixdp2x00_map_io(void); - -#endif - -#endif /*_IXDP2X00_H_ */ diff --git a/arch/arm/mach-ixp2000/include/mach/ixdp2x01.h b/arch/arm/mach-ixp2000/include/mach/ixdp2x01.h deleted file mode 100644 index 4c1f04083e54..000000000000 --- a/arch/arm/mach-ixp2000/include/mach/ixdp2x01.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * arch/arm/mach-ixp2000/include/mach/ixdp2x01.h - * - * Platform definitions for IXDP2X01 && IXDP2801 systems - * - * Author: Deepak Saxena - * - * Copyright 2004 (c) MontaVista Software, Inc. - * - * Based on original code Copyright (c) 2002-2003 Intel Corporation - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#ifndef __IXDP2X01_H__ -#define __IXDP2X01_H__ - -#define IXDP2X01_PHYS_CPLD_BASE 0xc6024000 -#define IXDP2X01_VIRT_CPLD_BASE 0xfe000000 -#define IXDP2X01_CPLD_REGION_SIZE 0x00100000 - -#define IXDP2X01_CPLD_VIRT_REG(reg) (volatile unsigned long*)(IXDP2X01_VIRT_CPLD_BASE | reg) -#define IXDP2X01_CPLD_PHYS_REG(reg) (IXDP2X01_PHYS_CPLD_BASE | reg) - -#define IXDP2X01_UART1_VIRT_BASE IXDP2X01_CPLD_VIRT_REG(0x40) -#define IXDP2X01_UART1_PHYS_BASE IXDP2X01_CPLD_PHYS_REG(0x40) - -#define IXDP2X01_UART2_VIRT_BASE IXDP2X01_CPLD_VIRT_REG(0x60) -#define IXDP2X01_UART2_PHYS_BASE IXDP2X01_CPLD_PHYS_REG(0x60) - -#define IXDP2X01_CS8900_VIRT_BASE IXDP2X01_CPLD_VIRT_REG(0x80) -#define IXDP2X01_CS8900_VIRT_END (IXDP2X01_CS8900_VIRT_BASE + 16) - -#define IXDP2X01_CPLD_RESET_REG IXDP2X01_CPLD_VIRT_REG(0x00) -#define IXDP2X01_INT_MASK_SET_REG IXDP2X01_CPLD_VIRT_REG(0x08) -#define IXDP2X01_INT_STAT_REG IXDP2X01_CPLD_VIRT_REG(0x0C) -#define IXDP2X01_INT_RAW_REG IXDP2X01_CPLD_VIRT_REG(0x10) -#define IXDP2X01_INT_MASK_CLR_REG IXDP2X01_INT_RAW_REG -#define IXDP2X01_INT_SIM_REG IXDP2X01_CPLD_VIRT_REG(0x14) - -#define IXDP2X01_CPLD_FLASH_REG IXDP2X01_CPLD_VIRT_REG(0x20) - -#define IXDP2X01_CPLD_FLASH_INTERN 0x8000 -#define IXDP2X01_CPLD_FLASH_BANK_MASK 0xF -#define IXDP2X01_FLASH_WINDOW_BITS 25 -#define IXDP2X01_FLASH_WINDOW_SIZE (1 << IXDP2X01_FLASH_WINDOW_BITS) -#define IXDP2X01_FLASH_WINDOW_MASK (IXDP2X01_FLASH_WINDOW_SIZE - 1) - -#define IXDP2X01_UART_CLK 1843200 - -#define IXDP2X01_GPIO_I2C_ENABLE 0x02 -#define IXDP2X01_GPIO_SCL 0x07 -#define IXDP2X01_GPIO_SDA 0x06 - -#endif /* __IXDP2x01_H__ */ diff --git a/arch/arm/mach-ixp2000/include/mach/ixp2000-regs.h b/arch/arm/mach-ixp2000/include/mach/ixp2000-regs.h deleted file mode 100644 index 822f63f2f4a2..000000000000 --- a/arch/arm/mach-ixp2000/include/mach/ixp2000-regs.h +++ /dev/null @@ -1,451 +0,0 @@ -/* - * arch/arm/mach-ixp2000/include/mach/ixp2000-regs.h - * - * Chipset register definitions for IXP2400/2800 based systems. - * - * Original Author: Naeem Afzal - * - * Maintainer: Deepak Saxena - * - * Copyright (C) 2002 Intel Corp. - * Copyright (C) 2003-2004 MontaVista Software, Inc. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ -#ifndef _IXP2000_REGS_H_ -#define _IXP2000_REGS_H_ - -/* - * IXP2000 linux memory map: - * - * virt phys size - * fb000000 db000000 16M PCI CFG1 - * fc000000 da000000 16M PCI CFG0 - * fd000000 d8000000 16M PCI I/O - * fe[0-7]00000 8M per-platform mappings - * fe900000 80000000 1M SRAM #0 (first MB) - * fea00000 cb400000 1M SCRATCH ring get/put - * feb00000 c8000000 1M MSF - * fec00000 df000000 1M PCI CSRs - * fed00000 de000000 1M PCI CREG - * fee00000 d6000000 1M INTCTL - * fef00000 c0000000 1M CAP - */ - -/* - * Static I/O regions. - * - * Most of the registers are clumped in 4K regions spread throughout - * the 0xc0000000 -> 0xc0100000 address range, but we just map in - * the whole range using a single 1 MB section instead of small - * 4K pages. - * - * CAP stands for CSR Access Proxy. - * - * If you change the virtual address of this mapping, please propagate - * the change to arch/arm/kernel/debug.S, which hardcodes the virtual - * address of the UART located in this region. - */ - -#define IXP2000_CAP_PHYS_BASE 0xc0000000 -#define IXP2000_CAP_VIRT_BASE 0xfef00000 -#define IXP2000_CAP_SIZE 0x00100000 - -/* - * Addresses for specific on-chip peripherals. - */ -#define IXP2000_SLOWPORT_CSR_VIRT_BASE 0xfef80000 -#define IXP2000_GLOBAL_REG_VIRT_BASE 0xfef04000 -#define IXP2000_UART_PHYS_BASE 0xc0030000 -#define IXP2000_UART_VIRT_BASE 0xfef30000 -#define IXP2000_TIMER_VIRT_BASE 0xfef20000 -#define IXP2000_UENGINE_CSR_VIRT_BASE 0xfef18000 -#define IXP2000_GPIO_VIRT_BASE 0xfef10000 - -/* - * Devices outside of the 0xc0000000 -> 0xc0100000 range. The virtual - * addresses of the INTCTL and PCI_CSR mappings are hardcoded in - * entry-macro.S, so if you ever change these please propagate - * the change. - */ -#define IXP2000_INTCTL_PHYS_BASE 0xd6000000 -#define IXP2000_INTCTL_VIRT_BASE 0xfee00000 -#define IXP2000_INTCTL_SIZE 0x00100000 - -#define IXP2000_PCI_CREG_PHYS_BASE 0xde000000 -#define IXP2000_PCI_CREG_VIRT_BASE 0xfed00000 -#define IXP2000_PCI_CREG_SIZE 0x00100000 - -#define IXP2000_PCI_CSR_PHYS_BASE 0xdf000000 -#define IXP2000_PCI_CSR_VIRT_BASE 0xfec00000 -#define IXP2000_PCI_CSR_SIZE 0x00100000 - -#define IXP2000_MSF_PHYS_BASE 0xc8000000 -#define IXP2000_MSF_VIRT_BASE 0xfeb00000 -#define IXP2000_MSF_SIZE 0x00100000 - -#define IXP2000_SCRATCH_RING_PHYS_BASE 0xcb400000 -#define IXP2000_SCRATCH_RING_VIRT_BASE 0xfea00000 -#define IXP2000_SCRATCH_RING_SIZE 0x00100000 - -#define IXP2000_SRAM0_PHYS_BASE 0x80000000 -#define IXP2000_SRAM0_VIRT_BASE 0xfe900000 -#define IXP2000_SRAM0_SIZE 0x00100000 - -#define IXP2000_PCI_IO_PHYS_BASE 0xd8000000 -#define IXP2000_PCI_IO_VIRT_BASE 0xfd000000 -#define IXP2000_PCI_IO_SIZE 0x01000000 - -#define IXP2000_PCI_CFG0_PHYS_BASE 0xda000000 -#define IXP2000_PCI_CFG0_VIRT_BASE 0xfc000000 -#define IXP2000_PCI_CFG0_SIZE 0x01000000 - -#define IXP2000_PCI_CFG1_PHYS_BASE 0xdb000000 -#define IXP2000_PCI_CFG1_VIRT_BASE 0xfb000000 -#define IXP2000_PCI_CFG1_SIZE 0x01000000 - -/* - * Timers - */ -#define IXP2000_TIMER_REG(x) ((volatile unsigned long*)(IXP2000_TIMER_VIRT_BASE | (x))) -/* Timer control */ -#define IXP2000_T1_CTL IXP2000_TIMER_REG(0x00) -#define IXP2000_T2_CTL IXP2000_TIMER_REG(0x04) -#define IXP2000_T3_CTL IXP2000_TIMER_REG(0x08) -#define IXP2000_T4_CTL IXP2000_TIMER_REG(0x0c) -/* Store initial value */ -#define IXP2000_T1_CLD IXP2000_TIMER_REG(0x10) -#define IXP2000_T2_CLD IXP2000_TIMER_REG(0x14) -#define IXP2000_T3_CLD IXP2000_TIMER_REG(0x18) -#define IXP2000_T4_CLD IXP2000_TIMER_REG(0x1c) -/* Read current value */ -#define IXP2000_T1_CSR IXP2000_TIMER_REG(0x20) -#define IXP2000_T2_CSR IXP2000_TIMER_REG(0x24) -#define IXP2000_T3_CSR IXP2000_TIMER_REG(0x28) -#define IXP2000_T4_CSR IXP2000_TIMER_REG(0x2c) -/* Clear associated timer interrupt */ -#define IXP2000_T1_CLR IXP2000_TIMER_REG(0x30) -#define IXP2000_T2_CLR IXP2000_TIMER_REG(0x34) -#define IXP2000_T3_CLR IXP2000_TIMER_REG(0x38) -#define IXP2000_T4_CLR IXP2000_TIMER_REG(0x3c) -/* Timer watchdog enable for T4 */ -#define IXP2000_TWDE IXP2000_TIMER_REG(0x40) - -#define WDT_ENABLE 0x00000001 -#define TIMER_DIVIDER_256 0x00000008 -#define TIMER_ENABLE 0x00000080 -#define IRQ_MASK_TIMER1 (1 << 4) - -/* - * Interrupt controller registers - */ -#define IXP2000_INTCTL_REG(x) (volatile unsigned long*)(IXP2000_INTCTL_VIRT_BASE | (x)) -#define IXP2000_IRQ_STATUS IXP2000_INTCTL_REG(0x08) -#define IXP2000_IRQ_ENABLE IXP2000_INTCTL_REG(0x10) -#define IXP2000_IRQ_ENABLE_SET IXP2000_INTCTL_REG(0x10) -#define IXP2000_IRQ_ENABLE_CLR IXP2000_INTCTL_REG(0x18) -#define IXP2000_FIQ_ENABLE_CLR IXP2000_INTCTL_REG(0x14) -#define IXP2000_IRQ_ERR_STATUS IXP2000_INTCTL_REG(0x24) -#define IXP2000_IRQ_ERR_ENABLE_SET IXP2000_INTCTL_REG(0x2c) -#define IXP2000_FIQ_ERR_ENABLE_CLR IXP2000_INTCTL_REG(0x30) -#define IXP2000_IRQ_ERR_ENABLE_CLR IXP2000_INTCTL_REG(0x34) -#define IXP2000_IRQ_THD_RAW_STATUS_A_0 IXP2000_INTCTL_REG(0x60) -#define IXP2000_IRQ_THD_RAW_STATUS_A_1 IXP2000_INTCTL_REG(0x64) -#define IXP2000_IRQ_THD_RAW_STATUS_A_2 IXP2000_INTCTL_REG(0x68) -#define IXP2000_IRQ_THD_RAW_STATUS_A_3 IXP2000_INTCTL_REG(0x6c) -#define IXP2000_IRQ_THD_RAW_STATUS_B_0 IXP2000_INTCTL_REG(0x80) -#define IXP2000_IRQ_THD_RAW_STATUS_B_1 IXP2000_INTCTL_REG(0x84) -#define IXP2000_IRQ_THD_RAW_STATUS_B_2 IXP2000_INTCTL_REG(0x88) -#define IXP2000_IRQ_THD_RAW_STATUS_B_3 IXP2000_INTCTL_REG(0x8c) -#define IXP2000_IRQ_THD_STATUS_A_0 IXP2000_INTCTL_REG(0xe0) -#define IXP2000_IRQ_THD_STATUS_A_1 IXP2000_INTCTL_REG(0xe4) -#define IXP2000_IRQ_THD_STATUS_A_2 IXP2000_INTCTL_REG(0xe8) -#define IXP2000_IRQ_THD_STATUS_A_3 IXP2000_INTCTL_REG(0xec) -#define IXP2000_IRQ_THD_STATUS_B_0 IXP2000_INTCTL_REG(0x100) -#define IXP2000_IRQ_THD_STATUS_B_1 IXP2000_INTCTL_REG(0x104) -#define IXP2000_IRQ_THD_STATUS_B_2 IXP2000_INTCTL_REG(0x108) -#define IXP2000_IRQ_THD_STATUS_B_3 IXP2000_INTCTL_REG(0x10c) -#define IXP2000_IRQ_THD_ENABLE_SET_A_0 IXP2000_INTCTL_REG(0x160) -#define IXP2000_IRQ_THD_ENABLE_SET_A_1 IXP2000_INTCTL_REG(0x164) -#define IXP2000_IRQ_THD_ENABLE_SET_A_2 IXP2000_INTCTL_REG(0x168) -#define IXP2000_IRQ_THD_ENABLE_SET_A_3 IXP2000_INTCTL_REG(0x16c) -#define IXP2000_IRQ_THD_ENABLE_SET_B_0 IXP2000_INTCTL_REG(0x180) -#define IXP2000_IRQ_THD_ENABLE_SET_B_1 IXP2000_INTCTL_REG(0x184) -#define IXP2000_IRQ_THD_ENABLE_SET_B_2 IXP2000_INTCTL_REG(0x188) -#define IXP2000_IRQ_THD_ENABLE_SET_B_3 IXP2000_INTCTL_REG(0x18c) -#define IXP2000_IRQ_THD_ENABLE_CLEAR_A_0 IXP2000_INTCTL_REG(0x1e0) -#define IXP2000_IRQ_THD_ENABLE_CLEAR_A_1 IXP2000_INTCTL_REG(0x1e4) -#define IXP2000_IRQ_THD_ENABLE_CLEAR_A_2 IXP2000_INTCTL_REG(0x1e8) -#define IXP2000_IRQ_THD_ENABLE_CLEAR_A_3 IXP2000_INTCTL_REG(0x1ec) -#define IXP2000_IRQ_THD_ENABLE_CLEAR_B_0 IXP2000_INTCTL_REG(0x200) -#define IXP2000_IRQ_THD_ENABLE_CLEAR_B_1 IXP2000_INTCTL_REG(0x204) -#define IXP2000_IRQ_THD_ENABLE_CLEAR_B_2 IXP2000_INTCTL_REG(0x208) -#define IXP2000_IRQ_THD_ENABLE_CLEAR_B_3 IXP2000_INTCTL_REG(0x20c) - -/* - * Mask of valid IRQs in the 32-bit IRQ register. We use - * this to mark certain IRQs as being invalid. - */ -#define IXP2000_VALID_IRQ_MASK 0x0f0fffff - -/* - * PCI config register access from core - */ -#define IXP2000_PCI_CREG(x) (volatile unsigned long*)(IXP2000_PCI_CREG_VIRT_BASE | (x)) -#define IXP2000_PCI_CMDSTAT IXP2000_PCI_CREG(0x04) -#define IXP2000_PCI_CSR_BAR IXP2000_PCI_CREG(0x10) -#define IXP2000_PCI_SRAM_BAR IXP2000_PCI_CREG(0x14) -#define IXP2000_PCI_SDRAM_BAR IXP2000_PCI_CREG(0x18) - -/* - * PCI CSRs - */ -#define IXP2000_PCI_CSR(x) (volatile unsigned long*)(IXP2000_PCI_CSR_VIRT_BASE | (x)) - -/* - * PCI outbound interrupts - */ -#define IXP2000_PCI_OUT_INT_STATUS IXP2000_PCI_CSR(0x30) -#define IXP2000_PCI_OUT_INT_MASK IXP2000_PCI_CSR(0x34) -/* - * PCI communications - */ -#define IXP2000_PCI_MAILBOX0 IXP2000_PCI_CSR(0x50) -#define IXP2000_PCI_MAILBOX1 IXP2000_PCI_CSR(0x54) -#define IXP2000_PCI_MAILBOX2 IXP2000_PCI_CSR(0x58) -#define IXP2000_PCI_MAILBOX3 IXP2000_PCI_CSR(0x5C) -#define IXP2000_XSCALE_DOORBELL IXP2000_PCI_CSR(0x60) -#define IXP2000_XSCALE_DOORBELL_SETUP IXP2000_PCI_CSR(0x64) -#define IXP2000_PCI_DOORBELL IXP2000_PCI_CSR(0x70) -#define IXP2000_PCI_DOORBELL_SETUP IXP2000_PCI_CSR(0x74) - -/* - * DMA engines - */ -#define IXP2000_PCI_CH1_BYTE_CNT IXP2000_PCI_CSR(0x80) -#define IXP2000_PCI_CH1_ADDR IXP2000_PCI_CSR(0x84) -#define IXP2000_PCI_CH1_DRAM_ADDR IXP2000_PCI_CSR(0x88) -#define IXP2000_PCI_CH1_DESC_PTR IXP2000_PCI_CSR(0x8C) -#define IXP2000_PCI_CH1_CNTRL IXP2000_PCI_CSR(0x90) -#define IXP2000_PCI_CH1_ME_PARAM IXP2000_PCI_CSR(0x94) -#define IXP2000_PCI_CH2_BYTE_CNT IXP2000_PCI_CSR(0xA0) -#define IXP2000_PCI_CH2_ADDR IXP2000_PCI_CSR(0xA4) -#define IXP2000_PCI_CH2_DRAM_ADDR IXP2000_PCI_CSR(0xA8) -#define IXP2000_PCI_CH2_DESC_PTR IXP2000_PCI_CSR(0xAC) -#define IXP2000_PCI_CH2_CNTRL IXP2000_PCI_CSR(0xB0) -#define IXP2000_PCI_CH2_ME_PARAM IXP2000_PCI_CSR(0xB4) -#define IXP2000_PCI_CH3_BYTE_CNT IXP2000_PCI_CSR(0xC0) -#define IXP2000_PCI_CH3_ADDR IXP2000_PCI_CSR(0xC4) -#define IXP2000_PCI_CH3_DRAM_ADDR IXP2000_PCI_CSR(0xC8) -#define IXP2000_PCI_CH3_DESC_PTR IXP2000_PCI_CSR(0xCC) -#define IXP2000_PCI_CH3_CNTRL IXP2000_PCI_CSR(0xD0) -#define IXP2000_PCI_CH3_ME_PARAM IXP2000_PCI_CSR(0xD4) -#define IXP2000_DMA_INF_MODE IXP2000_PCI_CSR(0xE0) -/* - * Size masks for BARs - */ -#define IXP2000_PCI_SRAM_BASE_ADDR_MASK IXP2000_PCI_CSR(0xFC) -#define IXP2000_PCI_DRAM_BASE_ADDR_MASK IXP2000_PCI_CSR(0x100) -/* - * Control and uEngine related - */ -#define IXP2000_PCI_CONTROL IXP2000_PCI_CSR(0x13C) -#define IXP2000_PCI_ADDR_EXT IXP2000_PCI_CSR(0x140) -#define IXP2000_PCI_ME_PUSH_STATUS IXP2000_PCI_CSR(0x148) -#define IXP2000_PCI_ME_PUSH_EN IXP2000_PCI_CSR(0x14C) -#define IXP2000_PCI_ERR_STATUS IXP2000_PCI_CSR(0x150) -#define IXP2000_PCI_ERR_ENABLE IXP2000_PCI_CSR(0x154) -/* - * Inbound PCI interrupt control - */ -#define IXP2000_PCI_XSCALE_INT_STATUS IXP2000_PCI_CSR(0x158) -#define IXP2000_PCI_XSCALE_INT_ENABLE IXP2000_PCI_CSR(0x15C) - -#define IXP2000_PCICNTL_PNR (1<<17) /* PCI not Reset bit of PCI_CONTROL */ -#define IXP2000_PCICNTL_PCF (1<<28) /* PCI Central function bit */ -#define IXP2000_XSCALE_INT (1<<1) /* Interrupt from XScale to PCI */ - -/* These are from the IRQ register in the PCI ISR register */ -#define PCI_CONTROL_BE_DEO (1 << 22) /* Big Endian Data Enable Out */ -#define PCI_CONTROL_BE_DEI (1 << 21) /* Big Endian Data Enable In */ -#define PCI_CONTROL_BE_BEO (1 << 20) /* Big Endian Byte Enable Out */ -#define PCI_CONTROL_BE_BEI (1 << 19) /* Big Endian Byte Enable In */ -#define PCI_CONTROL_IEE (1 << 17) /* I/O cycle Endian swap Enable */ - -#define IXP2000_PCI_RST_REL (1 << 2) -#define CFG_RST_DIR (*IXP2000_PCI_CONTROL & IXP2000_PCICNTL_PCF) -#define CFG_PCI_BOOT_HOST (1 << 2) -#define CFG_BOOT_PROM (1 << 1) - -/* - * SlowPort CSRs - * - * The slowport is used to access things like flash, SONET framer control - * ports, slave microprocessors, CPLDs, and others of chip memory mapped - * peripherals. - */ -#define SLOWPORT_CSR(x) (volatile unsigned long*)(IXP2000_SLOWPORT_CSR_VIRT_BASE | (x)) - -#define IXP2000_SLOWPORT_CCR SLOWPORT_CSR(0x00) -#define IXP2000_SLOWPORT_WTC1 SLOWPORT_CSR(0x04) -#define IXP2000_SLOWPORT_WTC2 SLOWPORT_CSR(0x08) -#define IXP2000_SLOWPORT_RTC1 SLOWPORT_CSR(0x0c) -#define IXP2000_SLOWPORT_RTC2 SLOWPORT_CSR(0x10) -#define IXP2000_SLOWPORT_FSR SLOWPORT_CSR(0x14) -#define IXP2000_SLOWPORT_PCR SLOWPORT_CSR(0x18) -#define IXP2000_SLOWPORT_ADC SLOWPORT_CSR(0x1C) -#define IXP2000_SLOWPORT_FAC SLOWPORT_CSR(0x20) -#define IXP2000_SLOWPORT_FRM SLOWPORT_CSR(0x24) -#define IXP2000_SLOWPORT_FIN SLOWPORT_CSR(0x28) - -/* - * CCR values. - * The CCR configures the clock division for the slowport interface. - */ -#define SLOWPORT_CCR_DIV_1 0x00 -#define SLOWPORT_CCR_DIV_2 0x01 -#define SLOWPORT_CCR_DIV_4 0x02 -#define SLOWPORT_CCR_DIV_6 0x03 -#define SLOWPORT_CCR_DIV_8 0x04 -#define SLOWPORT_CCR_DIV_10 0x05 -#define SLOWPORT_CCR_DIV_12 0x06 -#define SLOWPORT_CCR_DIV_14 0x07 -#define SLOWPORT_CCR_DIV_16 0x08 -#define SLOWPORT_CCR_DIV_18 0x09 -#define SLOWPORT_CCR_DIV_20 0x0a -#define SLOWPORT_CCR_DIV_22 0x0b -#define SLOWPORT_CCR_DIV_24 0x0c -#define SLOWPORT_CCR_DIV_26 0x0d -#define SLOWPORT_CCR_DIV_28 0x0e -#define SLOWPORT_CCR_DIV_30 0x0f - -/* - * PCR values. PCR configure the mode of the interface. - */ -#define SLOWPORT_MODE_FLASH 0x00 -#define SLOWPORT_MODE_LUCENT 0x01 -#define SLOWPORT_MODE_PMC_SIERRA 0x02 -#define SLOWPORT_MODE_INTEL_UP 0x03 -#define SLOWPORT_MODE_MOTOROLA_UP 0x04 - -/* - * ADC values. Defines data and address bus widths. - */ -#define SLOWPORT_ADDR_WIDTH_8 0x00 -#define SLOWPORT_ADDR_WIDTH_16 0x01 -#define SLOWPORT_ADDR_WIDTH_24 0x02 -#define SLOWPORT_ADDR_WIDTH_32 0x03 -#define SLOWPORT_DATA_WIDTH_8 0x00 -#define SLOWPORT_DATA_WIDTH_16 0x10 -#define SLOWPORT_DATA_WIDTH_24 0x20 -#define SLOWPORT_DATA_WIDTH_32 0x30 - -/* - * Masks and shifts for various fields in the WTC and RTC registers. - */ -#define SLOWPORT_WRTC_MASK_HD 0x0003 -#define SLOWPORT_WRTC_MASK_PW 0x003c -#define SLOWPORT_WRTC_MASK_SU 0x03c0 - -#define SLOWPORT_WRTC_SHIFT_HD 0x00 -#define SLOWPORT_WRTC_SHIFT_SU 0x02 -#define SLOWPORT_WRTC_SHFIT_PW 0x06 - - -/* - * GPIO registers & GPIO interface. - */ -#define IXP2000_GPIO_REG(x) ((volatile unsigned long*)(IXP2000_GPIO_VIRT_BASE+(x))) -#define IXP2000_GPIO_PLR IXP2000_GPIO_REG(0x00) -#define IXP2000_GPIO_PDPR IXP2000_GPIO_REG(0x04) -#define IXP2000_GPIO_PDSR IXP2000_GPIO_REG(0x08) -#define IXP2000_GPIO_PDCR IXP2000_GPIO_REG(0x0c) -#define IXP2000_GPIO_POPR IXP2000_GPIO_REG(0x10) -#define IXP2000_GPIO_POSR IXP2000_GPIO_REG(0x14) -#define IXP2000_GPIO_POCR IXP2000_GPIO_REG(0x18) -#define IXP2000_GPIO_REDR IXP2000_GPIO_REG(0x1c) -#define IXP2000_GPIO_FEDR IXP2000_GPIO_REG(0x20) -#define IXP2000_GPIO_EDSR IXP2000_GPIO_REG(0x24) -#define IXP2000_GPIO_LSHR IXP2000_GPIO_REG(0x28) -#define IXP2000_GPIO_LSLR IXP2000_GPIO_REG(0x2c) -#define IXP2000_GPIO_LDSR IXP2000_GPIO_REG(0x30) -#define IXP2000_GPIO_INER IXP2000_GPIO_REG(0x34) -#define IXP2000_GPIO_INSR IXP2000_GPIO_REG(0x38) -#define IXP2000_GPIO_INCR IXP2000_GPIO_REG(0x3c) -#define IXP2000_GPIO_INST IXP2000_GPIO_REG(0x40) - -/* - * "Global" registers...whatever that's supposed to mean. - */ -#define GLOBAL_REG_BASE (IXP2000_GLOBAL_REG_VIRT_BASE + 0x0a00) -#define GLOBAL_REG(x) (volatile unsigned long*)(GLOBAL_REG_BASE | (x)) - -#define IXP2000_MAJ_PROD_TYPE_MASK 0x001F0000 -#define IXP2000_MAJ_PROD_TYPE_IXP2000 0x00000000 -#define IXP2000_MIN_PROD_TYPE_MASK 0x0000FF00 -#define IXP2000_MIN_PROD_TYPE_IXP2400 0x00000200 -#define IXP2000_MIN_PROD_TYPE_IXP2850 0x00000100 -#define IXP2000_MIN_PROD_TYPE_IXP2800 0x00000000 -#define IXP2000_MAJ_REV_MASK 0x000000F0 -#define IXP2000_MIN_REV_MASK 0x0000000F -#define IXP2000_PROD_ID_MASK 0xFFFFFFFF - -#define IXP2000_PRODUCT_ID GLOBAL_REG(0x00) -#define IXP2000_MISC_CONTROL GLOBAL_REG(0x04) -#define IXP2000_MSF_CLK_CNTRL GLOBAL_REG(0x08) -#define IXP2000_RESET0 GLOBAL_REG(0x0c) -#define IXP2000_RESET1 GLOBAL_REG(0x10) -#define IXP2000_CCR GLOBAL_REG(0x14) -#define IXP2000_STRAP_OPTIONS GLOBAL_REG(0x18) - -#define RSTALL (1 << 16) -#define WDT_RESET_ENABLE 0x01000000 - - -/* - * MSF registers. The IXP2400 and IXP2800 have somewhat different MSF - * units, but the registers that differ between the two don't overlap, - * so we can have one register list for both. - */ -#define IXP2000_MSF_REG(x) ((volatile unsigned long*)(IXP2000_MSF_VIRT_BASE + (x))) -#define IXP2000_MSF_RX_CONTROL IXP2000_MSF_REG(0x0000) -#define IXP2000_MSF_TX_CONTROL IXP2000_MSF_REG(0x0004) -#define IXP2000_MSF_INTERRUPT_STATUS IXP2000_MSF_REG(0x0008) -#define IXP2000_MSF_INTERRUPT_ENABLE IXP2000_MSF_REG(0x000c) -#define IXP2000_MSF_CSIX_TYPE_MAP IXP2000_MSF_REG(0x0010) -#define IXP2000_MSF_FC_EGRESS_STATUS IXP2000_MSF_REG(0x0014) -#define IXP2000_MSF_FC_INGRESS_STATUS IXP2000_MSF_REG(0x0018) -#define IXP2000_MSF_HWM_CONTROL IXP2000_MSF_REG(0x0024) -#define IXP2000_MSF_FC_STATUS_OVERRIDE IXP2000_MSF_REG(0x0028) -#define IXP2000_MSF_CLOCK_CONTROL IXP2000_MSF_REG(0x002c) -#define IXP2000_MSF_RX_PORT_MAP IXP2000_MSF_REG(0x0040) -#define IXP2000_MSF_RBUF_ELEMENT_DONE IXP2000_MSF_REG(0x0044) -#define IXP2000_MSF_RX_MPHY_POLL_LIMIT IXP2000_MSF_REG(0x0048) -#define IXP2000_MSF_RX_CALENDAR_LENGTH IXP2000_MSF_REG(0x0048) -#define IXP2000_MSF_RX_THREAD_FREELIST_TIMEOUT_0 IXP2000_MSF_REG(0x0050) -#define IXP2000_MSF_RX_THREAD_FREELIST_TIMEOUT_1 IXP2000_MSF_REG(0x0054) -#define IXP2000_MSF_RX_THREAD_FREELIST_TIMEOUT_2 IXP2000_MSF_REG(0x0058) -#define IXP2000_MSF_TX_SEQUENCE_0 IXP2000_MSF_REG(0x0060) -#define IXP2000_MSF_TX_SEQUENCE_1 IXP2000_MSF_REG(0x0064) -#define IXP2000_MSF_TX_SEQUENCE_2 IXP2000_MSF_REG(0x0068) -#define IXP2000_MSF_TX_MPHY_POLL_LIMIT IXP2000_MSF_REG(0x0070) -#define IXP2000_MSF_TX_CALENDAR_LENGTH IXP2000_MSF_REG(0x0070) -#define IXP2000_MSF_RX_UP_CONTROL_0 IXP2000_MSF_REG(0x0080) -#define IXP2000_MSF_RX_UP_CONTROL_1 IXP2000_MSF_REG(0x0084) -#define IXP2000_MSF_RX_UP_CONTROL_2 IXP2000_MSF_REG(0x0088) -#define IXP2000_MSF_RX_UP_CONTROL_3 IXP2000_MSF_REG(0x008c) -#define IXP2000_MSF_TX_UP_CONTROL_0 IXP2000_MSF_REG(0x0090) -#define IXP2000_MSF_TX_UP_CONTROL_1 IXP2000_MSF_REG(0x0094) -#define IXP2000_MSF_TX_UP_CONTROL_2 IXP2000_MSF_REG(0x0098) -#define IXP2000_MSF_TX_UP_CONTROL_3 IXP2000_MSF_REG(0x009c) -#define IXP2000_MSF_TRAIN_DATA IXP2000_MSF_REG(0x00a0) -#define IXP2000_MSF_TRAIN_CALENDAR IXP2000_MSF_REG(0x00a4) -#define IXP2000_MSF_TRAIN_FLOW_CONTROL IXP2000_MSF_REG(0x00a8) -#define IXP2000_MSF_TX_CALENDAR_0 IXP2000_MSF_REG(0x1000) -#define IXP2000_MSF_RX_PORT_CALENDAR_STATUS IXP2000_MSF_REG(0x1400) - - -#endif /* _IXP2000_H_ */ diff --git a/arch/arm/mach-ixp2000/include/mach/memory.h b/arch/arm/mach-ixp2000/include/mach/memory.h deleted file mode 100644 index 5f0c4fd4076a..000000000000 --- a/arch/arm/mach-ixp2000/include/mach/memory.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * arch/arm/mach-ixp2000/include/mach/memory.h - * - * Copyright (c) 2002 Intel Corp. - * Copyright (c) 2003-2004 MontaVista Software, Inc. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef __ASM_ARCH_MEMORY_H -#define __ASM_ARCH_MEMORY_H - -#define PLAT_PHYS_OFFSET UL(0x00000000) - -#include - -#define IXP2000_PCI_SDRAM_OFFSET (*IXP2000_PCI_SDRAM_BAR & 0xfffffff0) - -#define __phys_to_bus(x) ((x) + (IXP2000_PCI_SDRAM_OFFSET - PHYS_OFFSET)) -#define __bus_to_phys(x) ((x) - (IXP2000_PCI_SDRAM_OFFSET - PHYS_OFFSET)) - -#define __virt_to_bus(v) __phys_to_bus(__virt_to_phys(v)) -#define __bus_to_virt(b) __phys_to_virt(__bus_to_phys(b)) -#define __pfn_to_bus(p) __phys_to_bus(__pfn_to_phys(p)) -#define __bus_to_pfn(b) __phys_to_pfn(__bus_to_phys(b)) - -#endif - diff --git a/arch/arm/mach-ixp2000/include/mach/platform.h b/arch/arm/mach-ixp2000/include/mach/platform.h deleted file mode 100644 index bb0f8dcf9ee1..000000000000 --- a/arch/arm/mach-ixp2000/include/mach/platform.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * arch/arm/mach-ixp2000/include/mach/platform.h - * - * Various bits of code used by platform-level code. - * - * Author: Deepak Saxena - * - * Copyright 2004 (c) MontaVista Software, Inc. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - - -#ifndef __ASSEMBLY__ - -static inline unsigned long ixp2000_reg_read(volatile void *reg) -{ - return *((volatile unsigned long *)reg); -} - -static inline void ixp2000_reg_write(volatile void *reg, unsigned long val) -{ - *((volatile unsigned long *)reg) = val; -} - -/* - * On the IXP2400, we can't use XCB=000 due to chip bugs. We use - * XCB=101 instead, but that makes all I/O accesses bufferable. This - * is not a problem in general, but we do have to be slightly more - * careful because I/O writes are no longer automatically flushed out - * of the write buffer. - * - * In cases where we want to make sure that a write has been flushed - * out of the write buffer before we proceed, for example when masking - * a device interrupt before re-enabling IRQs in CPSR, we can use this - * function, ixp2000_reg_wrb, which performs a write, a readback, and - * issues a dummy instruction dependent on the value of the readback - * (mov rX, rX) to make sure that the readback has completed before we - * continue. - */ -static inline void ixp2000_reg_wrb(volatile void *reg, unsigned long val) -{ - unsigned long dummy; - - *((volatile unsigned long *)reg) = val; - - dummy = *((volatile unsigned long *)reg); - __asm__ __volatile__("mov %0, %0" : "+r" (dummy)); -} - -/* - * Boards may multiplex different devices on the 2nd channel of - * the slowport interface that each need different configuration - * settings. For example, the IXDP2400 uses channel 2 on the interface - * to access the CPLD, the switch fabric card, and the media card. Each - * one needs a different mode so drivers must save/restore the mode - * before and after each operation. - * - * acquire_slowport(&your_config); - * ... - * do slowport operations - * ... - * release_slowport(); - * - * Note that while you have the slowport, you are holding a spinlock, - * so your code should be written as if you explicitly acquired a lock. - * - * The configuration only affects device 2 on the slowport, so the - * MTD map driver does not acquire/release the slowport. - */ -struct slowport_cfg { - unsigned long CCR; /* Clock divide */ - unsigned long WTC; /* Write Timing Control */ - unsigned long RTC; /* Read Timing Control */ - unsigned long PCR; /* Protocol Control Register */ - unsigned long ADC; /* Address/Data Width Control */ -}; - - -void ixp2000_acquire_slowport(struct slowport_cfg *, struct slowport_cfg *); -void ixp2000_release_slowport(struct slowport_cfg *); - -/* - * IXP2400 A0/A1 and IXP2800 A0/A1/A2 have broken slowport that requires - * tweaking of addresses in the MTD driver. - */ -static inline unsigned ixp2000_has_broken_slowport(void) -{ - unsigned long id = *IXP2000_PRODUCT_ID; - unsigned long id_prod = id & (IXP2000_MAJ_PROD_TYPE_MASK | - IXP2000_MIN_PROD_TYPE_MASK); - return (((id_prod == - /* fixed in IXP2400-B0 */ - (IXP2000_MAJ_PROD_TYPE_IXP2000 | - IXP2000_MIN_PROD_TYPE_IXP2400)) && - ((id & IXP2000_MAJ_REV_MASK) == 0)) || - ((id_prod == - /* fixed in IXP2800-B0 */ - (IXP2000_MAJ_PROD_TYPE_IXP2000 | - IXP2000_MIN_PROD_TYPE_IXP2800)) && - ((id & IXP2000_MAJ_REV_MASK) == 0)) || - ((id_prod == - /* fixed in IXP2850-B0 */ - (IXP2000_MAJ_PROD_TYPE_IXP2000 | - IXP2000_MIN_PROD_TYPE_IXP2850)) && - ((id & IXP2000_MAJ_REV_MASK) == 0))); -} - -static inline unsigned int ixp2000_has_flash(void) -{ - return ((*IXP2000_STRAP_OPTIONS) & (CFG_BOOT_PROM)); -} - -static inline unsigned int ixp2000_is_pcimaster(void) -{ - return ((*IXP2000_STRAP_OPTIONS) & (CFG_PCI_BOOT_HOST)); -} - -void ixp2000_map_io(void); -void ixp2000_uart_init(void); -void ixp2000_init_irq(void); -void ixp2000_init_time(unsigned long); -void ixp2000_restart(char, const char *); -unsigned long ixp2000_gettimeoffset(void); - -struct pci_sys_data; - -u32 *ixp2000_pci_config_addr(unsigned int bus, unsigned int devfn, int where); -void ixp2000_pci_preinit(void); -int ixp2000_pci_setup(int, struct pci_sys_data*); -struct pci_bus* ixp2000_pci_scan_bus(int, struct pci_sys_data*); -int ixp2000_pci_read_config(struct pci_bus*, unsigned int, int, int, u32 *); -int ixp2000_pci_write_config(struct pci_bus*, unsigned int, int, int, u32); - -/* - * Several of the IXP2000 systems have banked flash so we need to extend the - * flash_platform_data structure with some private pointers - */ -struct ixp2000_flash_data { - struct flash_platform_data *platform_data; - int nr_banks; - unsigned long (*bank_setup)(unsigned long); -}; - -struct ixp2000_i2c_pins { - unsigned long sda_pin; - unsigned long scl_pin; -}; - - -#endif /* !__ASSEMBLY__ */ diff --git a/arch/arm/mach-ixp2000/include/mach/timex.h b/arch/arm/mach-ixp2000/include/mach/timex.h deleted file mode 100644 index 835e659f93d4..000000000000 --- a/arch/arm/mach-ixp2000/include/mach/timex.h +++ /dev/null @@ -1,13 +0,0 @@ -/* - * arch/arm/mach-ixp2000/include/mach/timex.h - * - * IXP2000 architecture timex specifications - */ - - -/* - * Default clock is 50MHz APB, but platform code can override this - */ -#define CLOCK_TICK_RATE 50000000 - - diff --git a/arch/arm/mach-ixp2000/include/mach/uncompress.h b/arch/arm/mach-ixp2000/include/mach/uncompress.h deleted file mode 100644 index ce363087df78..000000000000 --- a/arch/arm/mach-ixp2000/include/mach/uncompress.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * arch/arm/mach-ixp2000/include/mach/uncompress.h - * - * - * Original Author: Naeem Afzal - * Maintainer: Deepak Saxena - * - * Copyright 2002 Intel 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; either version 2 of the License, or (at your - * option) any later version. - * - */ - -#include - -#define UART_BASE 0xc0030000 - -#define PHYS(x) ((volatile unsigned long *)(UART_BASE + x)) - -#define UARTDR PHYS(0x00) /* Transmit reg dlab=0 */ -#define UARTDLL PHYS(0x00) /* Divisor Latch reg dlab=1*/ -#define UARTDLM PHYS(0x04) /* Divisor Latch reg dlab=1*/ -#define UARTIER PHYS(0x04) /* Interrupt enable reg */ -#define UARTFCR PHYS(0x08) /* FIFO control reg dlab =0*/ -#define UARTLCR PHYS(0x0c) /* Control reg */ -#define UARTSR PHYS(0x14) /* Status reg */ - - -static inline void putc(int c) -{ - int j = 0x1000; - - while (--j && !(*UARTSR & UART_LSR_THRE)) - barrier(); - - *UARTDR = c; -} - -static inline void flush(void) -{ -} - -#define arch_decomp_setup() -#define arch_decomp_wdog() diff --git a/arch/arm/mach-ixp2000/ixdp2400.c b/arch/arm/mach-ixp2000/ixdp2400.c deleted file mode 100644 index 915ad49e3b8f..000000000000 --- a/arch/arm/mach-ixp2000/ixdp2400.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * arch/arm/mach-ixp2000/ixdp2400.c - * - * IXDP2400 platform support - * - * Original Author: Naeem Afzal - * Maintainer: Deepak Saxena - * - * Copyright (C) 2002 Intel Corp. - * Copyright (C) 2003-2004 MontaVista Software, Inc. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/************************************************************************* - * IXDP2400 timer tick - *************************************************************************/ -static void __init ixdp2400_timer_init(void) -{ - int numerator, denominator; - int denom_array[] = {2, 4, 8, 16, 1, 2, 4, 8}; - - numerator = (*(IXDP2400_CPLD_SYS_CLK_M) & 0xFF) *2; - denominator = denom_array[(*(IXDP2400_CPLD_SYS_CLK_N) & 0x7)]; - - ixp2000_init_time(((3125000 * numerator) / (denominator)) / 2); -} - -static struct sys_timer ixdp2400_timer = { - .init = ixdp2400_timer_init, - .offset = ixp2000_gettimeoffset, -}; - -/************************************************************************* - * IXDP2400 PCI - *************************************************************************/ -void __init ixdp2400_pci_preinit(void) -{ - ixp2000_reg_write(IXP2000_PCI_ADDR_EXT, 0x00100000); - ixp2000_pci_preinit(); - pcibios_setup("firmware"); -} - -int ixdp2400_pci_setup(int nr, struct pci_sys_data *sys) -{ - sys->mem_offset = 0xe0000000; - - ixp2000_pci_setup(nr, sys); - - return 1; -} - -static int __init ixdp2400_pci_map_irq(const struct pci_dev *dev, u8 slot, - u8 pin) -{ - if (ixdp2x00_master_npu()) { - - /* - * Root bus devices. Slave NPU is only one with interrupt. - * Everything else, we just return -1 b/c nothing else - * on the root bus has interrupts. - */ - if(!dev->bus->self) { - if(dev->devfn == IXDP2X00_SLAVE_NPU_DEVFN ) - return IRQ_IXDP2400_INGRESS_NPU; - - return -1; - } - - /* - * Bridge behind the PMC slot. - * NOTE: Only INTA from the PMC slot is routed. VERY BAD. - */ - if(dev->bus->self->devfn == IXDP2X00_PMC_DEVFN && - dev->bus->parent->self->devfn == IXDP2X00_P2P_DEVFN && - !dev->bus->parent->self->bus->parent) - return IRQ_IXDP2400_PMC; - - /* - * Device behind the first bridge - */ - if(dev->bus->self->devfn == IXDP2X00_P2P_DEVFN) { - switch(dev->devfn) { - case IXDP2400_MASTER_ENET_DEVFN: - return IRQ_IXDP2400_ENET; - - case IXDP2400_MEDIA_DEVFN: - return IRQ_IXDP2400_MEDIA_PCI; - - case IXDP2400_SWITCH_FABRIC_DEVFN: - return IRQ_IXDP2400_SF_PCI; - - case IXDP2X00_PMC_DEVFN: - return IRQ_IXDP2400_PMC; - } - } - - return -1; - } else return IRQ_IXP2000_PCIB; /* Slave NIC interrupt */ -} - - -static void ixdp2400_pci_postinit(void) -{ - struct pci_dev *dev; - - if (ixdp2x00_master_npu()) { - dev = pci_get_bus_and_slot(1, IXDP2400_SLAVE_ENET_DEVFN); - pci_stop_and_remove_bus_device(dev); - pci_dev_put(dev); - } else { - dev = pci_get_bus_and_slot(1, IXDP2400_MASTER_ENET_DEVFN); - pci_stop_and_remove_bus_device(dev); - pci_dev_put(dev); - - ixdp2x00_slave_pci_postinit(); - } -} - -static struct hw_pci ixdp2400_pci __initdata = { - .nr_controllers = 1, - .setup = ixdp2400_pci_setup, - .preinit = ixdp2400_pci_preinit, - .postinit = ixdp2400_pci_postinit, - .scan = ixp2000_pci_scan_bus, - .map_irq = ixdp2400_pci_map_irq, -}; - -int __init ixdp2400_pci_init(void) -{ - if (machine_is_ixdp2400()) - pci_common_init(&ixdp2400_pci); - - return 0; -} - -subsys_initcall(ixdp2400_pci_init); - -void __init ixdp2400_init_irq(void) -{ - ixdp2x00_init_irq(IXDP2400_CPLD_INT_STAT, IXDP2400_CPLD_INT_MASK, IXDP2400_NR_IRQS); -} - -MACHINE_START(IXDP2400, "Intel IXDP2400 Development Platform") - /* Maintainer: MontaVista Software, Inc. */ - .atag_offset = 0x100, - .map_io = ixdp2x00_map_io, - .init_irq = ixdp2400_init_irq, - .timer = &ixdp2400_timer, - .init_machine = ixdp2x00_init_machine, - .restart = ixp2000_restart, -MACHINE_END - diff --git a/arch/arm/mach-ixp2000/ixdp2800.c b/arch/arm/mach-ixp2000/ixdp2800.c deleted file mode 100644 index a9f1819ea049..000000000000 --- a/arch/arm/mach-ixp2000/ixdp2800.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * arch/arm/mach-ixp2000/ixdp2800.c - * - * IXDP2800 platform support - * - * Original Author: Jeffrey Daly - * Maintainer: Deepak Saxena - * - * Copyright (C) 2002 Intel Corp. - * Copyright (C) 2003-2004 MontaVista Software, Inc. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/************************************************************************* - * IXDP2800 timer tick - *************************************************************************/ - -static void __init ixdp2800_timer_init(void) -{ - ixp2000_init_time(50000000); -} - -static struct sys_timer ixdp2800_timer = { - .init = ixdp2800_timer_init, - .offset = ixp2000_gettimeoffset, -}; - -/************************************************************************* - * IXDP2800 PCI - *************************************************************************/ -static void __init ixdp2800_slave_disable_pci_master(void) -{ - *IXP2000_PCI_CMDSTAT &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); -} - -static void __init ixdp2800_master_wait_for_slave(void) -{ - volatile u32 *addr; - - printk(KERN_INFO "IXDP2800: waiting for slave NPU to configure " - "its BAR sizes\n"); - - addr = ixp2000_pci_config_addr(0, IXDP2X00_SLAVE_NPU_DEVFN, - PCI_BASE_ADDRESS_1); - do { - *addr = 0xffffffff; - cpu_relax(); - } while (*addr != 0xfe000008); - - addr = ixp2000_pci_config_addr(0, IXDP2X00_SLAVE_NPU_DEVFN, - PCI_BASE_ADDRESS_2); - do { - *addr = 0xffffffff; - cpu_relax(); - } while (*addr != 0xc0000008); - - /* - * Configure the slave's SDRAM BAR by hand. - */ - *addr = 0x40000008; -} - -static void __init ixdp2800_slave_wait_for_master_enable(void) -{ - printk(KERN_INFO "IXDP2800: waiting for master NPU to enable us\n"); - - while ((*IXP2000_PCI_CMDSTAT & PCI_COMMAND_MASTER) == 0) - cpu_relax(); -} - -void __init ixdp2800_pci_preinit(void) -{ - printk("ixdp2x00_pci_preinit called\n"); - - *IXP2000_PCI_ADDR_EXT = 0x0001e000; - - if (!ixdp2x00_master_npu()) - ixdp2800_slave_disable_pci_master(); - - *IXP2000_PCI_SRAM_BASE_ADDR_MASK = (0x2000000 - 1) & ~0x3ffff; - *IXP2000_PCI_DRAM_BASE_ADDR_MASK = (0x40000000 - 1) & ~0xfffff; - - ixp2000_pci_preinit(); - - if (ixdp2x00_master_npu()) { - /* - * Wait until the slave set its SRAM/SDRAM BAR sizes - * correctly before we proceed to scan and enumerate - * the bus. - */ - ixdp2800_master_wait_for_slave(); - - /* - * We configure the SDRAM BARs by hand because they - * are 1G and fall outside of the regular allocated - * PCI address space. - */ - *IXP2000_PCI_SDRAM_BAR = 0x00000008; - } else { - /* - * Wait for the master to complete scanning the bus - * and assigning resources before we proceed to scan - * the bus ourselves. Set pci=firmware to honor the - * master's resource assignment. - */ - ixdp2800_slave_wait_for_master_enable(); - pcibios_setup("firmware"); - } -} - -/* - * We assign the SDRAM BARs for the two IXP2800 CPUs by hand, outside - * of the regular PCI window, because there's only 512M of outbound PCI - * memory window on each IXP, while we need 1G for each of the BARs. - */ -static void __devinit ixp2800_pci_fixup(struct pci_dev *dev) -{ - if (machine_is_ixdp2800()) { - dev->resource[2].start = 0; - dev->resource[2].end = 0; - dev->resource[2].flags = 0; - } -} -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IXP2800, ixp2800_pci_fixup); - -static int __init ixdp2800_pci_setup(int nr, struct pci_sys_data *sys) -{ - sys->mem_offset = 0x00000000; - - ixp2000_pci_setup(nr, sys); - - return 1; -} - -static int __init ixdp2800_pci_map_irq(const struct pci_dev *dev, u8 slot, - u8 pin) -{ - if (ixdp2x00_master_npu()) { - - /* - * Root bus devices. Slave NPU is only one with interrupt. - * Everything else, we just return -1 which is invalid. - */ - if(!dev->bus->self) { - if(dev->devfn == IXDP2X00_SLAVE_NPU_DEVFN ) - return IRQ_IXDP2800_INGRESS_NPU; - - return -1; - } - - /* - * Bridge behind the PMC slot. - */ - if(dev->bus->self->devfn == IXDP2X00_PMC_DEVFN && - dev->bus->parent->self->devfn == IXDP2X00_P2P_DEVFN && - !dev->bus->parent->self->bus->parent) - return IRQ_IXDP2800_PMC; - - /* - * Device behind the first bridge - */ - if(dev->bus->self->devfn == IXDP2X00_P2P_DEVFN) { - switch(dev->devfn) { - case IXDP2X00_PMC_DEVFN: - return IRQ_IXDP2800_PMC; - - case IXDP2800_MASTER_ENET_DEVFN: - return IRQ_IXDP2800_EGRESS_ENET; - - case IXDP2800_SWITCH_FABRIC_DEVFN: - return IRQ_IXDP2800_FABRIC; - } - } - - return -1; - } else return IRQ_IXP2000_PCIB; /* Slave NIC interrupt */ -} - -static void __init ixdp2800_master_enable_slave(void) -{ - volatile u32 *addr; - - printk(KERN_INFO "IXDP2800: enabling slave NPU\n"); - - addr = (volatile u32 *)ixp2000_pci_config_addr(0, - IXDP2X00_SLAVE_NPU_DEVFN, - PCI_COMMAND); - - *addr |= PCI_COMMAND_MASTER; -} - -static void __init ixdp2800_master_wait_for_slave_bus_scan(void) -{ - volatile u32 *addr; - - printk(KERN_INFO "IXDP2800: waiting for slave to finish bus scan\n"); - - addr = (volatile u32 *)ixp2000_pci_config_addr(0, - IXDP2X00_SLAVE_NPU_DEVFN, - PCI_COMMAND); - while ((*addr & PCI_COMMAND_MEMORY) == 0) - cpu_relax(); -} - -static void __init ixdp2800_slave_signal_bus_scan_completion(void) -{ - printk(KERN_INFO "IXDP2800: bus scan done, signaling master\n"); - *IXP2000_PCI_CMDSTAT |= PCI_COMMAND_MEMORY; -} - -static void __init ixdp2800_pci_postinit(void) -{ - if (!ixdp2x00_master_npu()) { - ixdp2x00_slave_pci_postinit(); - ixdp2800_slave_signal_bus_scan_completion(); - } -} - -struct __initdata hw_pci ixdp2800_pci __initdata = { - .nr_controllers = 1, - .setup = ixdp2800_pci_setup, - .preinit = ixdp2800_pci_preinit, - .postinit = ixdp2800_pci_postinit, - .scan = ixp2000_pci_scan_bus, - .map_irq = ixdp2800_pci_map_irq, -}; - -int __init ixdp2800_pci_init(void) -{ - if (machine_is_ixdp2800()) { - struct pci_dev *dev; - - pci_common_init(&ixdp2800_pci); - if (ixdp2x00_master_npu()) { - dev = pci_get_bus_and_slot(1, IXDP2800_SLAVE_ENET_DEVFN); - pci_stop_and_remove_bus_device(dev); - pci_dev_put(dev); - - ixdp2800_master_enable_slave(); - ixdp2800_master_wait_for_slave_bus_scan(); - } else { - dev = pci_get_bus_and_slot(1, IXDP2800_MASTER_ENET_DEVFN); - pci_stop_and_remove_bus_device(dev); - pci_dev_put(dev); - } - } - - return 0; -} - -subsys_initcall(ixdp2800_pci_init); - -void __init ixdp2800_init_irq(void) -{ - ixdp2x00_init_irq(IXDP2800_CPLD_INT_STAT, IXDP2800_CPLD_INT_MASK, IXDP2800_NR_IRQS); -} - -MACHINE_START(IXDP2800, "Intel IXDP2800 Development Platform") - /* Maintainer: MontaVista Software, Inc. */ - .atag_offset = 0x100, - .map_io = ixdp2x00_map_io, - .init_irq = ixdp2800_init_irq, - .timer = &ixdp2800_timer, - .init_machine = ixdp2x00_init_machine, - .restart = ixp2000_restart, -MACHINE_END - diff --git a/arch/arm/mach-ixp2000/ixdp2x00.c b/arch/arm/mach-ixp2000/ixdp2x00.c deleted file mode 100644 index 421e38dc0fac..000000000000 --- a/arch/arm/mach-ixp2000/ixdp2x00.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * arch/arm/mach-ixp2000/ixdp2x00.c - * - * Code common to IXDP2400 and IXDP2800 platforms. - * - * Original Author: Naeem Afzal - * Maintainer: Deepak Saxena - * - * Copyright (C) 2002 Intel Corp. - * Copyright (C) 2003-2004 MontaVista Software, Inc. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include - -/************************************************************************* - * IXDP2x00 IRQ Initialization - *************************************************************************/ -static volatile unsigned long *board_irq_mask; -static volatile unsigned long *board_irq_stat; -static unsigned long board_irq_count; - -#ifdef CONFIG_ARCH_IXDP2400 -/* - * Slowport configuration for accessing CPLD registers on IXDP2x00 - */ -static struct slowport_cfg slowport_cpld_cfg = { - .CCR = SLOWPORT_CCR_DIV_2, - .WTC = 0x00000070, - .RTC = 0x00000070, - .PCR = SLOWPORT_MODE_FLASH, - .ADC = SLOWPORT_ADDR_WIDTH_24 | SLOWPORT_DATA_WIDTH_8 -}; -#endif - -static void ixdp2x00_irq_mask(struct irq_data *d) -{ - unsigned long dummy; - static struct slowport_cfg old_cfg; - - /* - * This is ugly in common code but really don't know - * of a better way to handle it. :( - */ -#ifdef CONFIG_ARCH_IXDP2400 - if (machine_is_ixdp2400()) - ixp2000_acquire_slowport(&slowport_cpld_cfg, &old_cfg); -#endif - - dummy = *board_irq_mask; - dummy |= IXP2000_BOARD_IRQ_MASK(d->irq); - ixp2000_reg_wrb(board_irq_mask, dummy); - -#ifdef CONFIG_ARCH_IXDP2400 - if (machine_is_ixdp2400()) - ixp2000_release_slowport(&old_cfg); -#endif -} - -static void ixdp2x00_irq_unmask(struct irq_data *d) -{ - unsigned long dummy; - static struct slowport_cfg old_cfg; - -#ifdef CONFIG_ARCH_IXDP2400 - if (machine_is_ixdp2400()) - ixp2000_acquire_slowport(&slowport_cpld_cfg, &old_cfg); -#endif - - dummy = *board_irq_mask; - dummy &= ~IXP2000_BOARD_IRQ_MASK(d->irq); - ixp2000_reg_wrb(board_irq_mask, dummy); - - if (machine_is_ixdp2400()) - ixp2000_release_slowport(&old_cfg); -} - -static void ixdp2x00_irq_handler(unsigned int irq, struct irq_desc *desc) -{ - volatile u32 ex_interrupt = 0; - static struct slowport_cfg old_cfg; - int i; - - desc->irq_data.chip->irq_mask(&desc->irq_data); - -#ifdef CONFIG_ARCH_IXDP2400 - if (machine_is_ixdp2400()) - ixp2000_acquire_slowport(&slowport_cpld_cfg, &old_cfg); -#endif - ex_interrupt = *board_irq_stat & 0xff; - if (machine_is_ixdp2400()) - ixp2000_release_slowport(&old_cfg); - - if(!ex_interrupt) { - printk(KERN_ERR "Spurious IXDP2x00 CPLD interrupt!\n"); - return; - } - - for(i = 0; i < board_irq_count; i++) { - if(ex_interrupt & (1 << i)) { - int cpld_irq = IXP2000_BOARD_IRQ(0) + i; - generic_handle_irq(cpld_irq); - } - } - - desc->irq_data.chip->irq_unmask(&desc->irq_data); -} - -static struct irq_chip ixdp2x00_cpld_irq_chip = { - .irq_ack = ixdp2x00_irq_mask, - .irq_mask = ixdp2x00_irq_mask, - .irq_unmask = ixdp2x00_irq_unmask -}; - -void __init ixdp2x00_init_irq(volatile unsigned long *stat_reg, volatile unsigned long *mask_reg, unsigned long nr_of_irqs) -{ - unsigned int irq; - - ixp2000_init_irq(); - - if (!ixdp2x00_master_npu()) - return; - - board_irq_stat = stat_reg; - board_irq_mask = mask_reg; - board_irq_count = nr_of_irqs; - - *board_irq_mask = 0xffffffff; - - for(irq = IXP2000_BOARD_IRQ(0); irq < IXP2000_BOARD_IRQ(board_irq_count); irq++) { - irq_set_chip_and_handler(irq, &ixdp2x00_cpld_irq_chip, - handle_level_irq); - set_irq_flags(irq, IRQF_VALID); - } - - /* Hook into PCI interrupt */ - irq_set_chained_handler(IRQ_IXP2000_PCIB, ixdp2x00_irq_handler); -} - -/************************************************************************* - * IXDP2x00 memory map - *************************************************************************/ -static struct map_desc ixdp2x00_io_desc __initdata = { - .virtual = IXDP2X00_VIRT_CPLD_BASE, - .pfn = __phys_to_pfn(IXDP2X00_PHYS_CPLD_BASE), - .length = IXDP2X00_CPLD_SIZE, - .type = MT_DEVICE -}; - -void __init ixdp2x00_map_io(void) -{ - ixp2000_map_io(); - - iotable_init(&ixdp2x00_io_desc, 1); -} - -/************************************************************************* - * IXDP2x00-common PCI init - * - * The IXDP2[48]00 has a horrid PCI bus layout. Basically the board - * contains two NPUs (ingress and egress) connected over PCI, both running - * instances of the kernel. So far so good. Peers on the PCI bus running - * Linux is a common design in telecom systems. The problem is that instead - * of all the devices being controlled by a single host, different - * devices are controlled by different NPUs on the same bus, leading to - * multiple hosts on the bus. The exact bus layout looks like: - * - * Bus 0 - * Master NPU <-------------------+-------------------> Slave NPU - * | - * | - * P2P - * | - * - * Bus 1 | - * <--+------+---------+---------+------+--> - * | | | | | - * | | | | | - * ... Dev PMC Media Eth0 Eth1 ... - * - * The master controls all but Eth1, which is controlled by the - * slave. What this means is that the both the master and the slave - * have to scan the bus, but only one of them can enumerate the bus. - * In addition, after the bus is scanned, each kernel must remove - * the device(s) it does not control from the PCI dev list otherwise - * a driver on each NPU will try to manage it and we will have horrible - * conflicts. Oh..and the slave NPU needs to see the master NPU - * for Intel's drivers to work properly. Closed source drivers... - * - * The way we deal with this is fairly simple but ugly: - * - * 1) Let master scan and enumerate the bus completely. - * 2) Master deletes Eth1 from device list. - * 3) Slave scans bus and then deletes all but Eth1 (Eth0 on slave) - * from device list. - * 4) Find HW designers and LART them. - * - * The boards also do not do normal PCI IRQ routing, or any sort of - * sensical swizzling, so we just need to check where on the bus a - * device sits and figure out to which CPLD pin the interrupt is routed. - * See ixdp2[48]00.c files. - * - *************************************************************************/ -void ixdp2x00_slave_pci_postinit(void) -{ - struct pci_dev *dev; - - /* - * Remove PMC device is there is one - */ - if((dev = pci_get_bus_and_slot(1, IXDP2X00_PMC_DEVFN))) { - pci_stop_and_remove_bus_device(dev); - pci_dev_put(dev); - } - - dev = pci_get_bus_and_slot(0, IXDP2X00_21555_DEVFN); - pci_stop_and_remove_bus_device(dev); - pci_dev_put(dev); -} - -/************************************************************************** - * IXDP2x00 Machine Setup - *************************************************************************/ -static struct flash_platform_data ixdp2x00_platform_data = { - .map_name = "cfi_probe", - .width = 1, -}; - -static struct ixp2000_flash_data ixdp2x00_flash_data = { - .platform_data = &ixdp2x00_platform_data, - .nr_banks = 1 -}; - -static struct resource ixdp2x00_flash_resource = { - .start = 0xc4000000, - .end = 0xc4000000 + 0x00ffffff, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device ixdp2x00_flash = { - .name = "IXP2000-Flash", - .id = 0, - .dev = { - .platform_data = &ixdp2x00_flash_data, - }, - .num_resources = 1, - .resource = &ixdp2x00_flash_resource, -}; - -static struct ixp2000_i2c_pins ixdp2x00_i2c_gpio_pins = { - .sda_pin = IXDP2X00_GPIO_SDA, - .scl_pin = IXDP2X00_GPIO_SCL, -}; - -static struct platform_device ixdp2x00_i2c_controller = { - .name = "IXP2000-I2C", - .id = 0, - .dev = { - .platform_data = &ixdp2x00_i2c_gpio_pins, - }, - .num_resources = 0 -}; - -static struct platform_device *ixdp2x00_devices[] __initdata = { - &ixdp2x00_flash, - &ixdp2x00_i2c_controller -}; - -void __init ixdp2x00_init_machine(void) -{ - gpio_line_set(IXDP2X00_GPIO_I2C_ENABLE, 1); - gpio_line_config(IXDP2X00_GPIO_I2C_ENABLE, GPIO_OUT); - - platform_add_devices(ixdp2x00_devices, ARRAY_SIZE(ixdp2x00_devices)); - ixp2000_uart_init(); -} - diff --git a/arch/arm/mach-ixp2000/ixdp2x01.c b/arch/arm/mach-ixp2000/ixdp2x01.c deleted file mode 100644 index 5196c39cdba4..000000000000 --- a/arch/arm/mach-ixp2000/ixdp2x01.c +++ /dev/null @@ -1,483 +0,0 @@ -/* - * arch/arm/mach-ixp2000/ixdp2x01.c - * - * Code common to Intel IXDP2401 and IXDP2801 platforms - * - * Original Author: Andrzej Mialkowski - * Maintainer: Deepak Saxena - * - * Copyright (C) 2002-2003 Intel Corp. - * Copyright (C) 2003-2004 MontaVista Software, Inc. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/************************************************************************* - * IXDP2x01 IRQ Handling - *************************************************************************/ -static void ixdp2x01_irq_mask(struct irq_data *d) -{ - ixp2000_reg_wrb(IXDP2X01_INT_MASK_SET_REG, - IXP2000_BOARD_IRQ_MASK(d->irq)); -} - -static void ixdp2x01_irq_unmask(struct irq_data *d) -{ - ixp2000_reg_write(IXDP2X01_INT_MASK_CLR_REG, - IXP2000_BOARD_IRQ_MASK(d->irq)); -} - -static u32 valid_irq_mask; - -static void ixdp2x01_irq_handler(unsigned int irq, struct irq_desc *desc) -{ - u32 ex_interrupt; - int i; - - desc->irq_data.chip->irq_mask(&desc->irq_data); - - ex_interrupt = *IXDP2X01_INT_STAT_REG & valid_irq_mask; - - if (!ex_interrupt) { - printk(KERN_ERR "Spurious IXDP2X01 CPLD interrupt!\n"); - return; - } - - for (i = 0; i < IXP2000_BOARD_IRQS; i++) { - if (ex_interrupt & (1 << i)) { - int cpld_irq = IXP2000_BOARD_IRQ(0) + i; - generic_handle_irq(cpld_irq); - } - } - - desc->irq_data.chip->irq_unmask(&desc->irq_data); -} - -static struct irq_chip ixdp2x01_irq_chip = { - .irq_mask = ixdp2x01_irq_mask, - .irq_ack = ixdp2x01_irq_mask, - .irq_unmask = ixdp2x01_irq_unmask -}; - -/* - * We only do anything if we are the master NPU on the board. - * The slave NPU only has the ethernet chip going directly to - * the PCIB interrupt input. - */ -void __init ixdp2x01_init_irq(void) -{ - int irq = 0; - - /* initialize chip specific interrupts */ - ixp2000_init_irq(); - - if (machine_is_ixdp2401()) - valid_irq_mask = IXDP2401_VALID_IRQ_MASK; - else - valid_irq_mask = IXDP2801_VALID_IRQ_MASK; - - /* Mask all interrupts from CPLD, disable simulation */ - ixp2000_reg_write(IXDP2X01_INT_MASK_SET_REG, 0xffffffff); - ixp2000_reg_wrb(IXDP2X01_INT_SIM_REG, 0); - - for (irq = NR_IXP2000_IRQS; irq < NR_IXDP2X01_IRQS; irq++) { - if (irq & valid_irq_mask) { - irq_set_chip_and_handler(irq, &ixdp2x01_irq_chip, - handle_level_irq); - set_irq_flags(irq, IRQF_VALID); - } else { - set_irq_flags(irq, 0); - } - } - - /* Hook into PCI interrupts */ - irq_set_chained_handler(IRQ_IXP2000_PCIB, ixdp2x01_irq_handler); -} - - -/************************************************************************* - * IXDP2x01 memory map - *************************************************************************/ -static struct map_desc ixdp2x01_io_desc __initdata = { - .virtual = IXDP2X01_VIRT_CPLD_BASE, - .pfn = __phys_to_pfn(IXDP2X01_PHYS_CPLD_BASE), - .length = IXDP2X01_CPLD_REGION_SIZE, - .type = MT_DEVICE -}; - -static void __init ixdp2x01_map_io(void) -{ - ixp2000_map_io(); - iotable_init(&ixdp2x01_io_desc, 1); -} - - -/************************************************************************* - * IXDP2x01 serial ports - *************************************************************************/ -static struct plat_serial8250_port ixdp2x01_serial_port1[] = { - { - .mapbase = (unsigned long)IXDP2X01_UART1_PHYS_BASE, - .membase = (char *)IXDP2X01_UART1_VIRT_BASE, - .irq = IRQ_IXDP2X01_UART1, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, - .iotype = UPIO_MEM32, - .regshift = 2, - .uartclk = IXDP2X01_UART_CLK, - }, - { } -}; - -static struct resource ixdp2x01_uart_resource1 = { - .start = IXDP2X01_UART1_PHYS_BASE, - .end = IXDP2X01_UART1_PHYS_BASE + 0xffff, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device ixdp2x01_serial_device1 = { - .name = "serial8250", - .id = PLAT8250_DEV_PLATFORM1, - .dev = { - .platform_data = ixdp2x01_serial_port1, - }, - .num_resources = 1, - .resource = &ixdp2x01_uart_resource1, -}; - -static struct plat_serial8250_port ixdp2x01_serial_port2[] = { - { - .mapbase = (unsigned long)IXDP2X01_UART2_PHYS_BASE, - .membase = (char *)IXDP2X01_UART2_VIRT_BASE, - .irq = IRQ_IXDP2X01_UART2, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, - .iotype = UPIO_MEM32, - .regshift = 2, - .uartclk = IXDP2X01_UART_CLK, - }, - { } -}; - -static struct resource ixdp2x01_uart_resource2 = { - .start = IXDP2X01_UART2_PHYS_BASE, - .end = IXDP2X01_UART2_PHYS_BASE + 0xffff, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device ixdp2x01_serial_device2 = { - .name = "serial8250", - .id = PLAT8250_DEV_PLATFORM2, - .dev = { - .platform_data = ixdp2x01_serial_port2, - }, - .num_resources = 1, - .resource = &ixdp2x01_uart_resource2, -}; - -static void ixdp2x01_uart_init(void) -{ - platform_device_register(&ixdp2x01_serial_device1); - platform_device_register(&ixdp2x01_serial_device2); -} - - -/************************************************************************* - * IXDP2x01 timer tick configuration - *************************************************************************/ -static unsigned int ixdp2x01_clock; - -static int __init ixdp2x01_clock_setup(char *str) -{ - ixdp2x01_clock = simple_strtoul(str, NULL, 10); - - return 1; -} - -__setup("ixdp2x01_clock=", ixdp2x01_clock_setup); - -static void __init ixdp2x01_timer_init(void) -{ - if (!ixdp2x01_clock) - ixdp2x01_clock = 50000000; - - ixp2000_init_time(ixdp2x01_clock); -} - -static struct sys_timer ixdp2x01_timer = { - .init = ixdp2x01_timer_init, - .offset = ixp2000_gettimeoffset, -}; - -/************************************************************************* - * IXDP2x01 PCI - *************************************************************************/ -void __init ixdp2x01_pci_preinit(void) -{ - ixp2000_reg_write(IXP2000_PCI_ADDR_EXT, 0x00000000); - ixp2000_pci_preinit(); - pcibios_setup("firmware"); -} - -#define DEVPIN(dev, pin) ((pin) | ((dev) << 3)) - -static int __init ixdp2x01_pci_map_irq(const struct pci_dev *dev, u8 slot, - u8 pin) -{ - u8 bus = dev->bus->number; - u32 devpin = DEVPIN(PCI_SLOT(dev->devfn), pin); - struct pci_bus *tmp_bus = dev->bus; - - /* Primary bus, no interrupts here */ - if (bus == 0) { - return -1; - } - - /* Lookup first leaf in bus tree */ - while ((tmp_bus->parent != NULL) && (tmp_bus->parent->parent != NULL)) { - tmp_bus = tmp_bus->parent; - } - - /* Select between known bridges */ - switch (tmp_bus->self->devfn | (tmp_bus->self->bus->number << 8)) { - /* Device is located after first MB bridge */ - case 0x0008: - if (tmp_bus == dev->bus) { - /* Device is located directly after first MB bridge */ - switch (devpin) { - case DEVPIN(1, 1): /* Onboard 82546 ch 0 */ - if (machine_is_ixdp2401()) - return IRQ_IXDP2401_INTA_82546; - return -1; - case DEVPIN(1, 2): /* Onboard 82546 ch 1 */ - if (machine_is_ixdp2401()) - return IRQ_IXDP2401_INTB_82546; - return -1; - case DEVPIN(0, 1): /* PMC INTA# */ - return IRQ_IXDP2X01_SPCI_PMC_INTA; - case DEVPIN(0, 2): /* PMC INTB# */ - return IRQ_IXDP2X01_SPCI_PMC_INTB; - case DEVPIN(0, 3): /* PMC INTC# */ - return IRQ_IXDP2X01_SPCI_PMC_INTC; - case DEVPIN(0, 4): /* PMC INTD# */ - return IRQ_IXDP2X01_SPCI_PMC_INTD; - } - } - break; - case 0x0010: - if (tmp_bus == dev->bus) { - /* Device is located directly after second MB bridge */ - /* Secondary bus of second bridge */ - switch (devpin) { - case DEVPIN(0, 1): /* DB#0 */ - return IRQ_IXDP2X01_SPCI_DB_0; - case DEVPIN(1, 1): /* DB#1 */ - return IRQ_IXDP2X01_SPCI_DB_1; - } - } else { - /* Device is located indirectly after second MB bridge */ - /* Not supported now */ - } - break; - } - - return -1; -} - - -static int ixdp2x01_pci_setup(int nr, struct pci_sys_data *sys) -{ - sys->mem_offset = 0xe0000000; - - if (machine_is_ixdp2801() || machine_is_ixdp28x5()) - sys->mem_offset -= ((*IXP2000_PCI_ADDR_EXT & 0xE000) << 16); - - return ixp2000_pci_setup(nr, sys); -} - -struct hw_pci ixdp2x01_pci __initdata = { - .nr_controllers = 1, - .setup = ixdp2x01_pci_setup, - .preinit = ixdp2x01_pci_preinit, - .scan = ixp2000_pci_scan_bus, - .map_irq = ixdp2x01_pci_map_irq, -}; - -int __init ixdp2x01_pci_init(void) -{ - if (machine_is_ixdp2401() || machine_is_ixdp2801() ||\ - machine_is_ixdp28x5()) - pci_common_init(&ixdp2x01_pci); - - return 0; -} - -subsys_initcall(ixdp2x01_pci_init); - -/************************************************************************* - * IXDP2x01 Machine Initialization - *************************************************************************/ -static struct flash_platform_data ixdp2x01_flash_platform_data = { - .map_name = "cfi_probe", - .width = 1, -}; - -static unsigned long ixdp2x01_flash_bank_setup(unsigned long ofs) -{ - ixp2000_reg_wrb(IXDP2X01_CPLD_FLASH_REG, - ((ofs >> IXDP2X01_FLASH_WINDOW_BITS) | IXDP2X01_CPLD_FLASH_INTERN)); - return (ofs & IXDP2X01_FLASH_WINDOW_MASK); -} - -static struct ixp2000_flash_data ixdp2x01_flash_data = { - .platform_data = &ixdp2x01_flash_platform_data, - .bank_setup = ixdp2x01_flash_bank_setup -}; - -static struct resource ixdp2x01_flash_resource = { - .start = 0xc4000000, - .end = 0xc4000000 + 0x01ffffff, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device ixdp2x01_flash = { - .name = "IXP2000-Flash", - .id = 0, - .dev = { - .platform_data = &ixdp2x01_flash_data, - }, - .num_resources = 1, - .resource = &ixdp2x01_flash_resource, -}; - -static struct ixp2000_i2c_pins ixdp2x01_i2c_gpio_pins = { - .sda_pin = IXDP2X01_GPIO_SDA, - .scl_pin = IXDP2X01_GPIO_SCL, -}; - -static struct platform_device ixdp2x01_i2c_controller = { - .name = "IXP2000-I2C", - .id = 0, - .dev = { - .platform_data = &ixdp2x01_i2c_gpio_pins, - }, - .num_resources = 0 -}; - -static struct platform_device *ixdp2x01_devices[] __initdata = { - &ixdp2x01_flash, - &ixdp2x01_i2c_controller -}; - -static void __init ixdp2x01_init_machine(void) -{ - ixp2000_reg_wrb(IXDP2X01_CPLD_FLASH_REG, - (IXDP2X01_CPLD_FLASH_BANK_MASK | IXDP2X01_CPLD_FLASH_INTERN)); - - ixdp2x01_flash_data.nr_banks = - ((*IXDP2X01_CPLD_FLASH_REG & IXDP2X01_CPLD_FLASH_BANK_MASK) + 1); - - platform_add_devices(ixdp2x01_devices, ARRAY_SIZE(ixdp2x01_devices)); - ixp2000_uart_init(); - ixdp2x01_uart_init(); -} - -static void ixdp2401_restart(char mode, const char *cmd) -{ - /* - * Reset flash banking register so that we are pointing at - * RedBoot bank. - */ - ixp2000_reg_write(IXDP2X01_CPLD_FLASH_REG, - ((0 >> IXDP2X01_FLASH_WINDOW_BITS) - | IXDP2X01_CPLD_FLASH_INTERN)); - ixp2000_reg_wrb(IXDP2X01_CPLD_RESET_REG, 0xffffffff); - - ixp2000_restart(mode, cmd); -} - -static void ixdp280x_restart(char mode, const char *cmd) -{ - /* - * On IXDP2801 we need to write this magic sequence to the CPLD - * to cause a complete reset of the CPU and all external devices - * and move the flash bank register back to 0. - */ - unsigned long reset_reg = *IXDP2X01_CPLD_RESET_REG; - - reset_reg = 0x55AA0000 | (reset_reg & 0x0000FFFF); - ixp2000_reg_write(IXDP2X01_CPLD_RESET_REG, reset_reg); - ixp2000_reg_wrb(IXDP2X01_CPLD_RESET_REG, 0x80000000); - - ixp2000_restart(mode, cmd); -} - -#ifdef CONFIG_ARCH_IXDP2401 -MACHINE_START(IXDP2401, "Intel IXDP2401 Development Platform") - /* Maintainer: MontaVista Software, Inc. */ - .atag_offset = 0x100, - .map_io = ixdp2x01_map_io, - .init_irq = ixdp2x01_init_irq, - .timer = &ixdp2x01_timer, - .init_machine = ixdp2x01_init_machine, - .restart = ixdp2401_restart, -MACHINE_END -#endif - -#ifdef CONFIG_ARCH_IXDP2801 -MACHINE_START(IXDP2801, "Intel IXDP2801 Development Platform") - /* Maintainer: MontaVista Software, Inc. */ - .atag_offset = 0x100, - .map_io = ixdp2x01_map_io, - .init_irq = ixdp2x01_init_irq, - .timer = &ixdp2x01_timer, - .init_machine = ixdp2x01_init_machine, - .restart = ixdp280x_restart, -MACHINE_END - -/* - * IXDP28x5 is basically an IXDP2801 with a different CPU but Intel - * changed the machine ID in the bootloader - */ -MACHINE_START(IXDP28X5, "Intel IXDP2805/2855 Development Platform") - /* Maintainer: MontaVista Software, Inc. */ - .atag_offset = 0x100, - .map_io = ixdp2x01_map_io, - .init_irq = ixdp2x01_init_irq, - .timer = &ixdp2x01_timer, - .init_machine = ixdp2x01_init_machine, - .restart = ixdp280x_restart, -MACHINE_END -#endif - - diff --git a/arch/arm/mach-ixp2000/pci.c b/arch/arm/mach-ixp2000/pci.c deleted file mode 100644 index 9c02de932fac..000000000000 --- a/arch/arm/mach-ixp2000/pci.c +++ /dev/null @@ -1,252 +0,0 @@ -/* - * arch/arm/mach-ixp2000/pci.c - * - * PCI routines for IXDP2400/IXDP2800 boards - * - * Original Author: Naeem Afzal - * Maintained by: Deepak Saxena - * - * Copyright 2002 Intel Corp. - * Copyright (C) 2003-2004 MontaVista Software, Inc. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -static volatile int pci_master_aborts = 0; - -static int clear_master_aborts(void); - -u32 * -ixp2000_pci_config_addr(unsigned int bus_nr, unsigned int devfn, int where) -{ - u32 *paddress; - - if (PCI_SLOT(devfn) > 7) - return 0; - - /* Must be dword aligned */ - where &= ~3; - - /* - * For top bus, generate type 0, else type 1 - */ - if (!bus_nr) { - /* only bits[23:16] are used for IDSEL */ - paddress = (u32 *) (IXP2000_PCI_CFG0_VIRT_BASE - | (1 << (PCI_SLOT(devfn) + 16)) - | (PCI_FUNC(devfn) << 8) | where); - } else { - paddress = (u32 *) (IXP2000_PCI_CFG1_VIRT_BASE - | (bus_nr << 16) - | (PCI_SLOT(devfn) << 11) - | (PCI_FUNC(devfn) << 8) | where); - } - - return paddress; -} - -/* - * Mask table, bits to mask for quantity of size 1, 2 or 4 bytes. - * 0 and 3 are not valid indexes... - */ -static u32 bytemask[] = { - /*0*/ 0, - /*1*/ 0xff, - /*2*/ 0xffff, - /*3*/ 0, - /*4*/ 0xffffffff, -}; - - -int ixp2000_pci_read_config(struct pci_bus *bus, unsigned int devfn, int where, - int size, u32 *value) -{ - u32 n; - u32 *addr; - - n = where % 4; - - addr = ixp2000_pci_config_addr(bus->number, devfn, where); - if (!addr) - return PCIBIOS_DEVICE_NOT_FOUND; - - pci_master_aborts = 0; - *value = (*addr >> (8*n)) & bytemask[size]; - if (pci_master_aborts) { - pci_master_aborts = 0; - *value = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - return PCIBIOS_SUCCESSFUL; -} - -/* - * We don't do error checks by calling clear_master_aborts() b/c the - * assumption is that the caller did a read first to make sure a device - * exists. - */ -int ixp2000_pci_write_config(struct pci_bus *bus, unsigned int devfn, int where, - int size, u32 value) -{ - u32 mask; - u32 *addr; - u32 temp; - - mask = ~(bytemask[size] << ((where % 0x4) * 8)); - addr = ixp2000_pci_config_addr(bus->number, devfn, where); - if (!addr) - return PCIBIOS_DEVICE_NOT_FOUND; - temp = (u32) (value) << ((where % 0x4) * 8); - *addr = (*addr & mask) | temp; - - clear_master_aborts(); - - return PCIBIOS_SUCCESSFUL; -} - - -static struct pci_ops ixp2000_pci_ops = { - .read = ixp2000_pci_read_config, - .write = ixp2000_pci_write_config -}; - -struct pci_bus *ixp2000_pci_scan_bus(int nr, struct pci_sys_data *sysdata) -{ - return pci_scan_root_bus(NULL, sysdata->busnr, &ixp2000_pci_ops, - sysdata, &sysdata->resources); -} - - -int ixp2000_pci_abort_handler(unsigned long addr, unsigned int fsr, struct pt_regs *regs) -{ - - volatile u32 temp; - unsigned long flags; - - pci_master_aborts = 1; - - local_irq_save(flags); - temp = *(IXP2000_PCI_CONTROL); - if (temp & ((1 << 8) | (1 << 5))) { - ixp2000_reg_wrb(IXP2000_PCI_CONTROL, temp); - } - - temp = *(IXP2000_PCI_CMDSTAT); - if (temp & (1 << 29)) { - while (temp & (1 << 29)) { - ixp2000_reg_write(IXP2000_PCI_CMDSTAT, temp); - temp = *(IXP2000_PCI_CMDSTAT); - } - } - local_irq_restore(flags); - - /* - * If it was an imprecise abort, then we need to correct the - * return address to be _after_ the instruction. - */ - if (fsr & (1 << 10)) - regs->ARM_pc += 4; - - return 0; -} - -int -clear_master_aborts(void) -{ - volatile u32 temp; - unsigned long flags; - - local_irq_save(flags); - temp = *(IXP2000_PCI_CONTROL); - if (temp & ((1 << 8) | (1 << 5))) { - ixp2000_reg_wrb(IXP2000_PCI_CONTROL, temp); - } - - temp = *(IXP2000_PCI_CMDSTAT); - if (temp & (1 << 29)) { - while (temp & (1 << 29)) { - ixp2000_reg_write(IXP2000_PCI_CMDSTAT, temp); - temp = *(IXP2000_PCI_CMDSTAT); - } - } - local_irq_restore(flags); - - return 0; -} - -void __init -ixp2000_pci_preinit(void) -{ - pci_set_flags(0); - - pcibios_min_io = 0; - pcibios_min_mem = 0; - -#ifndef CONFIG_IXP2000_SUPPORT_BROKEN_PCI_IO - /* - * Configure the PCI unit to properly byteswap I/O transactions, - * and verify that it worked. - */ - ixp2000_reg_write(IXP2000_PCI_CONTROL, - (*IXP2000_PCI_CONTROL | PCI_CONTROL_IEE)); - - if ((*IXP2000_PCI_CONTROL & PCI_CONTROL_IEE) == 0) - panic("IXP2000: PCI I/O is broken on this ixp model, and " - "the needed workaround has not been configured in"); -#endif - - hook_fault_code(16+6, ixp2000_pci_abort_handler, SIGBUS, 0, - "PCI config cycle to non-existent device"); -} - - -/* - * IXP2000 systems often have large resource requirements, so we just - * use our own resource space. - */ -static struct resource ixp2000_pci_mem_space = { - .start = 0xe0000000, - .end = 0xffffffff, - .flags = IORESOURCE_MEM, - .name = "PCI Mem Space" -}; - -static struct resource ixp2000_pci_io_space = { - .start = 0x00010000, - .end = 0x0001ffff, - .flags = IORESOURCE_IO, - .name = "PCI I/O Space" -}; - -int ixp2000_pci_setup(int nr, struct pci_sys_data *sys) -{ - if (nr >= 1) - return 0; - - pci_add_resource_offset(&sys->resources, - &ixp2000_pci_io_space, sys->io_offset); - pci_add_resource_offset(&sys->resources, - &ixp2000_pci_mem_space, sys->mem_offset); - - return 1; -} - diff --git a/arch/arm/mach-ixp23xx/Kconfig b/arch/arm/mach-ixp23xx/Kconfig deleted file mode 100644 index 982670ec3866..000000000000 --- a/arch/arm/mach-ixp23xx/Kconfig +++ /dev/null @@ -1,25 +0,0 @@ -if ARCH_IXP23XX - -config ARCH_SUPPORTS_BIG_ENDIAN - bool - default y - -menu "Intel IXP23xx Implementation Options" - -comment "IXP23xx Platforms" - -config MACH_ESPRESSO - bool "Support IP Fabrics Double Espresso platform" - help - -config MACH_IXDP2351 - bool "Support Intel IXDP2351 platform" - help - -config MACH_ROADRUNNER - bool "Support ADI RoadRunner platform" - help - -endmenu - -endif diff --git a/arch/arm/mach-ixp23xx/Makefile b/arch/arm/mach-ixp23xx/Makefile deleted file mode 100644 index 288b371b6d03..000000000000 --- a/arch/arm/mach-ixp23xx/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -# -# Makefile for the linux kernel. -# -obj-y := core.o pci.o -obj-m := -obj-n := -obj- := - -obj-$(CONFIG_MACH_ESPRESSO) += espresso.o -obj-$(CONFIG_MACH_IXDP2351) += ixdp2351.o -obj-$(CONFIG_MACH_ROADRUNNER) += roadrunner.o diff --git a/arch/arm/mach-ixp23xx/Makefile.boot b/arch/arm/mach-ixp23xx/Makefile.boot deleted file mode 100644 index 44fb4a717c3f..000000000000 --- a/arch/arm/mach-ixp23xx/Makefile.boot +++ /dev/null @@ -1,2 +0,0 @@ - zreladdr-y += 0x00008000 -params_phys-y := 0x00000100 diff --git a/arch/arm/mach-ixp23xx/core.c b/arch/arm/mach-ixp23xx/core.c deleted file mode 100644 index d34542425990..000000000000 --- a/arch/arm/mach-ixp23xx/core.c +++ /dev/null @@ -1,455 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/core.c - * - * Core routines for IXP23xx chips - * - * Author: Deepak Saxena - * - * Copyright 2005 (c) MontaVista Software, Inc. - * - * Based on 2.4 code Copyright 2004 (c) Intel Corporation - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - - -/************************************************************************* - * Chip specific mappings shared by all IXP23xx systems - *************************************************************************/ -static struct map_desc ixp23xx_io_desc[] __initdata = { - { /* XSI-CPP CSRs */ - .virtual = IXP23XX_XSI2CPP_CSR_VIRT, - .pfn = __phys_to_pfn(IXP23XX_XSI2CPP_CSR_PHYS), - .length = IXP23XX_XSI2CPP_CSR_SIZE, - .type = MT_DEVICE, - }, { /* Expansion Bus Config */ - .virtual = IXP23XX_EXP_CFG_VIRT, - .pfn = __phys_to_pfn(IXP23XX_EXP_CFG_PHYS), - .length = IXP23XX_EXP_CFG_SIZE, - .type = MT_DEVICE, - }, { /* UART, Interrupt ctrl, GPIO, timers, NPEs, MACS,.... */ - .virtual = IXP23XX_PERIPHERAL_VIRT, - .pfn = __phys_to_pfn(IXP23XX_PERIPHERAL_PHYS), - .length = IXP23XX_PERIPHERAL_SIZE, - .type = MT_DEVICE, - }, { /* CAP CSRs */ - .virtual = IXP23XX_CAP_CSR_VIRT, - .pfn = __phys_to_pfn(IXP23XX_CAP_CSR_PHYS), - .length = IXP23XX_CAP_CSR_SIZE, - .type = MT_DEVICE, - }, { /* MSF CSRs */ - .virtual = IXP23XX_MSF_CSR_VIRT, - .pfn = __phys_to_pfn(IXP23XX_MSF_CSR_PHYS), - .length = IXP23XX_MSF_CSR_SIZE, - .type = MT_DEVICE, - }, { /* PCI I/O Space */ - .virtual = IXP23XX_PCI_IO_VIRT, - .pfn = __phys_to_pfn(IXP23XX_PCI_IO_PHYS), - .length = IXP23XX_PCI_IO_SIZE, - .type = MT_DEVICE, - }, { /* PCI Config Space */ - .virtual = IXP23XX_PCI_CFG_VIRT, - .pfn = __phys_to_pfn(IXP23XX_PCI_CFG_PHYS), - .length = IXP23XX_PCI_CFG_SIZE, - .type = MT_DEVICE, - }, { /* PCI local CFG CSRs */ - .virtual = IXP23XX_PCI_CREG_VIRT, - .pfn = __phys_to_pfn(IXP23XX_PCI_CREG_PHYS), - .length = IXP23XX_PCI_CREG_SIZE, - .type = MT_DEVICE, - }, { /* PCI MEM Space */ - .virtual = IXP23XX_PCI_MEM_VIRT, - .pfn = __phys_to_pfn(IXP23XX_PCI_MEM_PHYS), - .length = IXP23XX_PCI_MEM_SIZE, - .type = MT_DEVICE, - } -}; - -void __init ixp23xx_map_io(void) -{ - iotable_init(ixp23xx_io_desc, ARRAY_SIZE(ixp23xx_io_desc)); -} - - -/*************************************************************************** - * IXP23xx Interrupt Handling - ***************************************************************************/ -enum ixp23xx_irq_type { - IXP23XX_IRQ_LEVEL, IXP23XX_IRQ_EDGE -}; - -static void ixp23xx_config_irq(unsigned int, enum ixp23xx_irq_type); - -static int ixp23xx_irq_set_type(struct irq_data *d, unsigned int type) -{ - int line = d->irq - IRQ_IXP23XX_GPIO6 + 6; - u32 int_style; - enum ixp23xx_irq_type irq_type; - volatile u32 *int_reg; - - /* - * Only GPIOs 6-15 are wired to interrupts on IXP23xx - */ - if (line < 6 || line > 15) - return -EINVAL; - - switch (type) { - case IRQ_TYPE_EDGE_BOTH: - int_style = IXP23XX_GPIO_STYLE_TRANSITIONAL; - irq_type = IXP23XX_IRQ_EDGE; - break; - case IRQ_TYPE_EDGE_RISING: - int_style = IXP23XX_GPIO_STYLE_RISING_EDGE; - irq_type = IXP23XX_IRQ_EDGE; - break; - case IRQ_TYPE_EDGE_FALLING: - int_style = IXP23XX_GPIO_STYLE_FALLING_EDGE; - irq_type = IXP23XX_IRQ_EDGE; - break; - case IRQ_TYPE_LEVEL_HIGH: - int_style = IXP23XX_GPIO_STYLE_ACTIVE_HIGH; - irq_type = IXP23XX_IRQ_LEVEL; - break; - case IRQ_TYPE_LEVEL_LOW: - int_style = IXP23XX_GPIO_STYLE_ACTIVE_LOW; - irq_type = IXP23XX_IRQ_LEVEL; - break; - default: - return -EINVAL; - } - - ixp23xx_config_irq(d->irq, irq_type); - - if (line >= 8) { /* pins 8-15 */ - line -= 8; - int_reg = (volatile u32 *)IXP23XX_GPIO_GPIT2R; - } else { /* pins 0-7 */ - int_reg = (volatile u32 *)IXP23XX_GPIO_GPIT1R; - } - - /* - * Clear pending interrupts - */ - *IXP23XX_GPIO_GPISR = (1 << line); - - /* Clear the style for the appropriate pin */ - *int_reg &= ~(IXP23XX_GPIO_STYLE_MASK << - (line * IXP23XX_GPIO_STYLE_SIZE)); - - /* Set the new style */ - *int_reg |= (int_style << (line * IXP23XX_GPIO_STYLE_SIZE)); - - return 0; -} - -static void ixp23xx_irq_mask(struct irq_data *d) -{ - volatile unsigned long *intr_reg; - unsigned int irq = d->irq; - - if (irq >= 56) - irq += 8; - - intr_reg = IXP23XX_INTR_EN1 + (irq / 32); - *intr_reg &= ~(1 << (irq % 32)); -} - -static void ixp23xx_irq_ack(struct irq_data *d) -{ - int line = d->irq - IRQ_IXP23XX_GPIO6 + 6; - - if ((line < 6) || (line > 15)) - return; - - *IXP23XX_GPIO_GPISR = (1 << line); -} - -/* - * Level triggered interrupts on GPIO lines can only be cleared when the - * interrupt condition disappears. - */ -static void ixp23xx_irq_level_unmask(struct irq_data *d) -{ - volatile unsigned long *intr_reg; - unsigned int irq = d->irq; - - ixp23xx_irq_ack(d); - - if (irq >= 56) - irq += 8; - - intr_reg = IXP23XX_INTR_EN1 + (irq / 32); - *intr_reg |= (1 << (irq % 32)); -} - -static void ixp23xx_irq_edge_unmask(struct irq_data *d) -{ - volatile unsigned long *intr_reg; - unsigned int irq = d->irq; - - if (irq >= 56) - irq += 8; - - intr_reg = IXP23XX_INTR_EN1 + (irq / 32); - *intr_reg |= (1 << (irq % 32)); -} - -static struct irq_chip ixp23xx_irq_level_chip = { - .irq_ack = ixp23xx_irq_mask, - .irq_mask = ixp23xx_irq_mask, - .irq_unmask = ixp23xx_irq_level_unmask, - .irq_set_type = ixp23xx_irq_set_type -}; - -static struct irq_chip ixp23xx_irq_edge_chip = { - .irq_ack = ixp23xx_irq_ack, - .irq_mask = ixp23xx_irq_mask, - .irq_unmask = ixp23xx_irq_edge_unmask, - .irq_set_type = ixp23xx_irq_set_type -}; - -static void ixp23xx_pci_irq_mask(struct irq_data *d) -{ - unsigned int irq = d->irq; - - *IXP23XX_PCI_XSCALE_INT_ENABLE &= ~(1 << (IRQ_IXP23XX_INTA + 27 - irq)); -} - -static void ixp23xx_pci_irq_unmask(struct irq_data *d) -{ - unsigned int irq = d->irq; - - *IXP23XX_PCI_XSCALE_INT_ENABLE |= (1 << (IRQ_IXP23XX_INTA + 27 - irq)); -} - -/* - * TODO: Should this just be done at ASM level? - */ -static void pci_handler(unsigned int irq, struct irq_desc *desc) -{ - u32 pci_interrupt; - unsigned int irqno; - - pci_interrupt = *IXP23XX_PCI_XSCALE_INT_STATUS; - - desc->irq_data.chip->irq_ack(&desc->irq_data); - - /* See which PCI_INTA, or PCI_INTB interrupted */ - if (pci_interrupt & (1 << 26)) { - irqno = IRQ_IXP23XX_INTB; - } else if (pci_interrupt & (1 << 27)) { - irqno = IRQ_IXP23XX_INTA; - } else { - BUG(); - } - - generic_handle_irq(irqno); - - desc->irq_data.chip->irq_unmask(&desc->irq_data); -} - -static struct irq_chip ixp23xx_pci_irq_chip = { - .irq_ack = ixp23xx_pci_irq_mask, - .irq_mask = ixp23xx_pci_irq_mask, - .irq_unmask = ixp23xx_pci_irq_unmask -}; - -static void ixp23xx_config_irq(unsigned int irq, enum ixp23xx_irq_type type) -{ - switch (type) { - case IXP23XX_IRQ_LEVEL: - irq_set_chip_and_handler(irq, &ixp23xx_irq_level_chip, - handle_level_irq); - break; - case IXP23XX_IRQ_EDGE: - irq_set_chip_and_handler(irq, &ixp23xx_irq_edge_chip, - handle_edge_irq); - break; - } - set_irq_flags(irq, IRQF_VALID); -} - -void __init ixp23xx_init_irq(void) -{ - int irq; - - /* Route everything to IRQ */ - *IXP23XX_INTR_SEL1 = 0x0; - *IXP23XX_INTR_SEL2 = 0x0; - *IXP23XX_INTR_SEL3 = 0x0; - *IXP23XX_INTR_SEL4 = 0x0; - - /* Mask all sources */ - *IXP23XX_INTR_EN1 = 0x0; - *IXP23XX_INTR_EN2 = 0x0; - *IXP23XX_INTR_EN3 = 0x0; - *IXP23XX_INTR_EN4 = 0x0; - - /* - * Configure all IRQs for level-sensitive operation - */ - for (irq = 0; irq <= NUM_IXP23XX_RAW_IRQS; irq++) { - ixp23xx_config_irq(irq, IXP23XX_IRQ_LEVEL); - } - - for (irq = IRQ_IXP23XX_INTA; irq <= IRQ_IXP23XX_INTB; irq++) { - irq_set_chip_and_handler(irq, &ixp23xx_pci_irq_chip, - handle_level_irq); - set_irq_flags(irq, IRQF_VALID); - } - - irq_set_chained_handler(IRQ_IXP23XX_PCI_INT_RPH, pci_handler); -} - - -/************************************************************************* - * Timer-tick functions for IXP23xx - *************************************************************************/ -#define CLOCK_TICKS_PER_USEC (CLOCK_TICK_RATE / USEC_PER_SEC) - -static unsigned long next_jiffy_time; - -static unsigned long -ixp23xx_gettimeoffset(void) -{ - unsigned long elapsed; - - elapsed = *IXP23XX_TIMER_CONT - (next_jiffy_time - LATCH); - - return elapsed / CLOCK_TICKS_PER_USEC; -} - -static irqreturn_t -ixp23xx_timer_interrupt(int irq, void *dev_id) -{ - /* Clear Pending Interrupt by writing '1' to it */ - *IXP23XX_TIMER_STATUS = IXP23XX_TIMER1_INT_PEND; - while ((signed long)(*IXP23XX_TIMER_CONT - next_jiffy_time) >= LATCH) { - timer_tick(); - next_jiffy_time += LATCH; - } - - return IRQ_HANDLED; -} - -static struct irqaction ixp23xx_timer_irq = { - .name = "IXP23xx Timer Tick", - .handler = ixp23xx_timer_interrupt, - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, -}; - -void __init ixp23xx_init_timer(void) -{ - /* Clear Pending Interrupt by writing '1' to it */ - *IXP23XX_TIMER_STATUS = IXP23XX_TIMER1_INT_PEND; - - /* Setup the Timer counter value */ - *IXP23XX_TIMER1_RELOAD = - (LATCH & ~IXP23XX_TIMER_RELOAD_MASK) | IXP23XX_TIMER_ENABLE; - - *IXP23XX_TIMER_CONT = 0; - next_jiffy_time = LATCH; - - /* Connect the interrupt handler and enable the interrupt */ - setup_irq(IRQ_IXP23XX_TIMER1, &ixp23xx_timer_irq); -} - -struct sys_timer ixp23xx_timer = { - .init = ixp23xx_init_timer, - .offset = ixp23xx_gettimeoffset, -}; - - -/************************************************************************* - * IXP23xx Platform Initialization - *************************************************************************/ -static struct resource ixp23xx_uart_resources[] = { - { - .start = IXP23XX_UART1_PHYS, - .end = IXP23XX_UART1_PHYS + 0x0fff, - .flags = IORESOURCE_MEM - }, { - .start = IXP23XX_UART2_PHYS, - .end = IXP23XX_UART2_PHYS + 0x0fff, - .flags = IORESOURCE_MEM - } -}; - -static struct plat_serial8250_port ixp23xx_uart_data[] = { - { - .mapbase = IXP23XX_UART1_PHYS, - .membase = (char *)(IXP23XX_UART1_VIRT + 3), - .irq = IRQ_IXP23XX_UART1, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, - .iotype = UPIO_MEM, - .regshift = 2, - .uartclk = IXP23XX_UART_XTAL, - }, { - .mapbase = IXP23XX_UART2_PHYS, - .membase = (char *)(IXP23XX_UART2_VIRT + 3), - .irq = IRQ_IXP23XX_UART2, - .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST, - .iotype = UPIO_MEM, - .regshift = 2, - .uartclk = IXP23XX_UART_XTAL, - }, - { }, -}; - -static struct platform_device ixp23xx_uart = { - .name = "serial8250", - .id = 0, - .dev.platform_data = ixp23xx_uart_data, - .num_resources = 2, - .resource = ixp23xx_uart_resources, -}; - -static struct platform_device *ixp23xx_devices[] __initdata = { - &ixp23xx_uart, -}; - -void __init ixp23xx_sys_init(void) -{ - /* by default, the idle code is disabled */ - disable_hlt(); - - *IXP23XX_EXP_UNIT_FUSE |= 0xf; - platform_add_devices(ixp23xx_devices, ARRAY_SIZE(ixp23xx_devices)); -} - -void ixp23xx_restart(char mode, const char *cmd) -{ - /* Use on-chip reset capability */ - *IXP23XX_RESET0 |= IXP23XX_RST_ALL; -} diff --git a/arch/arm/mach-ixp23xx/espresso.c b/arch/arm/mach-ixp23xx/espresso.c deleted file mode 100644 index d142d45dea12..000000000000 --- a/arch/arm/mach-ixp23xx/espresso.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/espresso.c - * - * Double Espresso-specific routines - * - * Author: Lennert Buytenhek - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -static int __init espresso_pci_init(void) -{ - if (machine_is_espresso()) - ixp23xx_pci_slave_init(); - - return 0; -}; -subsys_initcall(espresso_pci_init); - -static struct physmap_flash_data espresso_flash_data = { - .width = 2, -}; - -static struct resource espresso_flash_resource = { - .start = 0x90000000, - .end = 0x91ffffff, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device espresso_flash = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &espresso_flash_data, - }, - .num_resources = 1, - .resource = &espresso_flash_resource, -}; - -static void __init espresso_init(void) -{ - platform_device_register(&espresso_flash); - - /* - * Mark flash as writeable. - */ - IXP23XX_EXP_CS0[0] |= IXP23XX_FLASH_WRITABLE; - IXP23XX_EXP_CS0[1] |= IXP23XX_FLASH_WRITABLE; - - ixp23xx_sys_init(); -} - -MACHINE_START(ESPRESSO, "IP Fabrics Double Espresso") - /* Maintainer: Lennert Buytenhek */ - .map_io = ixp23xx_map_io, - .init_irq = ixp23xx_init_irq, - .timer = &ixp23xx_timer, - .atag_offset = 0x100, - .init_machine = espresso_init, - .restart = ixp23xx_restart, -MACHINE_END diff --git a/arch/arm/mach-ixp23xx/include/mach/debug-macro.S b/arch/arm/mach-ixp23xx/include/mach/debug-macro.S deleted file mode 100644 index 5ff524c13744..000000000000 --- a/arch/arm/mach-ixp23xx/include/mach/debug-macro.S +++ /dev/null @@ -1,25 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/include/mach/debug-macro.S - * - * Debugging macro include header - * - * Copyright (C) 1994-1999 Russell King - * Moved from linux/arch/arm/kernel/debug.S by Ben Dooks - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include - - .macro addruart, rp, rv, tmp - ldr \rp, =IXP23XX_PERIPHERAL_PHYS @ physical - ldr \rv, =IXP23XX_PERIPHERAL_VIRT @ virtual -#ifdef __ARMEB__ - orr \rp, \rp, #0x00000003 - orr \rv, \rv, #0x00000003 -#endif - .endm - -#define UART_SHIFT 2 -#include diff --git a/arch/arm/mach-ixp23xx/include/mach/entry-macro.S b/arch/arm/mach-ixp23xx/include/mach/entry-macro.S deleted file mode 100644 index 3fd2cb984e42..000000000000 --- a/arch/arm/mach-ixp23xx/include/mach/entry-macro.S +++ /dev/null @@ -1,31 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/include/mach/entry-macro.S - */ - - .macro get_irqnr_preamble, base, tmp - .endm - - .macro get_irqnr_and_base, irqnr, irqstat, base, tmp - ldr \irqnr, =(IXP23XX_INTC_VIRT + IXP23XX_INTR_IRQ_ENC_ST_OFFSET) - ldr \irqnr, [\irqnr] @ get interrupt number - cmp \irqnr, #0x0 @ spurious interrupt ? - movne \irqnr, \irqnr, lsr #2 @ skip unwanted low order bits - subne \irqnr, \irqnr, #1 @ convert to 0 based - -#if 0 - cmp \irqnr, #IRQ_IXP23XX_PCI_INT_RPH - bne 1001f - mov \irqnr, #IRQ_IXP23XX_INTA - - ldr \irqnr, =0xf5000030 - - mov \tmp, #(1<<26) - tst \irqnr, \tmp - movne \irqnr, #IRQ_IXP23XX_INTB - - mov \tmp, #(1<<27) - tst \irqnr, \tmp - movne \irqnr, #IRQ_IXP23XX_INTA -1001: -#endif - .endm diff --git a/arch/arm/mach-ixp23xx/include/mach/hardware.h b/arch/arm/mach-ixp23xx/include/mach/hardware.h deleted file mode 100644 index 60e55fa10238..000000000000 --- a/arch/arm/mach-ixp23xx/include/mach/hardware.h +++ /dev/null @@ -1,32 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/include/mach/hardware.h - * - * Copyright (C) 2002-2004 Intel Corporation. - * Copyricht (C) 2005 MontaVista Software, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Hardware definitions for IXP23XX based systems - */ - -#ifndef __ASM_ARCH_HARDWARE_H -#define __ASM_ARCH_HARDWARE_H - -/* PCI IO info */ - -#include "ixp23xx.h" - -/* - * Platform helper functions - */ -#include "platform.h" - -/* - * Platform-specific headers - */ -#include "ixdp2351.h" - - -#endif diff --git a/arch/arm/mach-ixp23xx/include/mach/io.h b/arch/arm/mach-ixp23xx/include/mach/io.h deleted file mode 100644 index a7aceb55c130..000000000000 --- a/arch/arm/mach-ixp23xx/include/mach/io.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/include/mach/io.h - * - * Original Author: Naeem M Afzal - * Maintainer: Deepak Saxena - * - * Copyright (C) 2003-2005 Intel Corp. - * Copyright (C) 2005 MontaVista Software, Inc - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __ASM_ARCH_IO_H -#define __ASM_ARCH_IO_H - -#define IO_SPACE_LIMIT 0xffffffff - -#define __io(p) ((void __iomem*)((p) + IXP23XX_PCI_IO_VIRT)) - -#endif diff --git a/arch/arm/mach-ixp23xx/include/mach/irqs.h b/arch/arm/mach-ixp23xx/include/mach/irqs.h deleted file mode 100644 index 3af33a04b8a2..000000000000 --- a/arch/arm/mach-ixp23xx/include/mach/irqs.h +++ /dev/null @@ -1,223 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/include/mach/irqs.h - * - * IRQ definitions for IXP23XX based systems - * - * Author: Naeem Afzal - * - * Copyright (C) 2003-2004 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __ASM_ARCH_IRQS_H -#define __ASM_ARCH_IRQS_H - -#define NR_IXP23XX_IRQS IRQ_IXP23XX_INTB+1 -#define IRQ_IXP23XX_EXTIRQS NR_IXP23XX_IRQS - - -#define IRQ_IXP23XX_DBG0 0 /* Debug/Execution/MBox */ -#define IRQ_IXP23XX_DBG1 1 /* Debug/Execution/MBox */ -#define IRQ_IXP23XX_NPE_TRG 2 /* npe_trigger */ -#define IRQ_IXP23XX_TIMER1 3 /* Timer[0] */ -#define IRQ_IXP23XX_TIMER2 4 /* Timer[1] */ -#define IRQ_IXP23XX_TIMESTAMP 5 /* Timer[2], Time-stamp */ -#define IRQ_IXP23XX_WDOG 6 /* Time[3], Watchdog Timer */ -#define IRQ_IXP23XX_PCI_DBELL 7 /* PCI Doorbell */ -#define IRQ_IXP23XX_PCI_DMA1 8 /* PCI DMA Channel 1 */ -#define IRQ_IXP23XX_PCI_DMA2 9 /* PCI DMA Channel 2 */ -#define IRQ_IXP23XX_PCI_DMA3 10 /* PCI DMA Channel 3 */ -#define IRQ_IXP23XX_PCI_INT_RPH 11 /* pcxg_pci_int_rph */ -#define IRQ_IXP23XX_CPP_PMU 12 /* xpxg_pm_int_rpl */ -#define IRQ_IXP23XX_SWINT0 13 /* S/W Interrupt0 */ -#define IRQ_IXP23XX_SWINT1 14 /* S/W Interrupt1 */ -#define IRQ_IXP23XX_UART2 15 /* UART1 Interrupt */ -#define IRQ_IXP23XX_UART1 16 /* UART0 Interrupt */ -#define IRQ_IXP23XX_XSI_PMU_ROLLOVER 17 /* AHB Performance M. Unit counter rollover */ -#define IRQ_IXP23XX_XSI_AHB_PM0 18 /* intr_pm_o */ -#define IRQ_IXP23XX_XSI_AHB_ECE0 19 /* intr_ece_o */ -#define IRQ_IXP23XX_XSI_AHB_GASKET 20 /* gas_intr_o */ -#define IRQ_IXP23XX_XSI_CPP 21 /* xsi2cpp_int */ -#define IRQ_IXP23XX_CPP_XSI 22 /* cpp2xsi_int */ -#define IRQ_IXP23XX_ME_ATTN0 23 /* ME_ATTN */ -#define IRQ_IXP23XX_ME_ATTN1 24 /* ME_ATTN */ -#define IRQ_IXP23XX_ME_ATTN2 25 /* ME_ATTN */ -#define IRQ_IXP23XX_ME_ATTN3 26 /* ME_ATTN */ -#define IRQ_IXP23XX_PCI_ERR_RPH 27 /* PCXG_PCI_ERR_RPH */ -#define IRQ_IXP23XX_D0XG_ECC_CORR 28 /* D0XG_DRAM_ECC_CORR */ -#define IRQ_IXP23XX_D0XG_ECC_UNCORR 29 /* D0XG_DRAM_ECC_UNCORR */ -#define IRQ_IXP23XX_SRAM_ERR1 30 /* SRAM1_ERR */ -#define IRQ_IXP23XX_SRAM_ERR0 31 /* SRAM0_ERR */ -#define IRQ_IXP23XX_MEDIA_ERR 32 /* MEDIA_ERR */ -#define IRQ_IXP23XX_STH_DRAM_ECC_MAJ 33 /* STH_DRAM0_ECC_MAJ */ -#define IRQ_IXP23XX_GPIO6 34 /* GPIO0 interrupts */ -#define IRQ_IXP23XX_GPIO7 35 /* GPIO1 interrupts */ -#define IRQ_IXP23XX_GPIO8 36 /* GPIO2 interrupts */ -#define IRQ_IXP23XX_GPIO9 37 /* GPIO3 interrupts */ -#define IRQ_IXP23XX_GPIO10 38 /* GPIO4 interrupts */ -#define IRQ_IXP23XX_GPIO11 39 /* GPIO5 interrupts */ -#define IRQ_IXP23XX_GPIO12 40 /* GPIO6 interrupts */ -#define IRQ_IXP23XX_GPIO13 41 /* GPIO7 interrupts */ -#define IRQ_IXP23XX_GPIO14 42 /* GPIO8 interrupts */ -#define IRQ_IXP23XX_GPIO15 43 /* GPIO9 interrupts */ -#define IRQ_IXP23XX_SHAC_RING0 44 /* SHAC Ring Full */ -#define IRQ_IXP23XX_SHAC_RING1 45 /* SHAC Ring Full */ -#define IRQ_IXP23XX_SHAC_RING2 46 /* SHAC Ring Full */ -#define IRQ_IXP23XX_SHAC_RING3 47 /* SHAC Ring Full */ -#define IRQ_IXP23XX_SHAC_RING4 48 /* SHAC Ring Full */ -#define IRQ_IXP23XX_SHAC_RING5 49 /* SHAC Ring Full */ -#define IRQ_IXP23XX_SHAC_RING6 50 /* SHAC RING Full */ -#define IRQ_IXP23XX_SHAC_RING7 51 /* SHAC Ring Full */ -#define IRQ_IXP23XX_SHAC_RING8 52 /* SHAC Ring Full */ -#define IRQ_IXP23XX_SHAC_RING9 53 /* SHAC Ring Full */ -#define IRQ_IXP23XX_SHAC_RING10 54 /* SHAC Ring Full */ -#define IRQ_IXP23XX_SHAC_RING11 55 /* SHAC Ring Full */ -#define IRQ_IXP23XX_ME_THREAD_A0_ME0 56 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A1_ME0 57 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A2_ME0 58 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A3_ME0 59 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A4_ME0 60 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A5_ME0 61 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A6_ME0 62 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A7_ME0 63 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A8_ME1 64 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A9_ME1 65 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A10_ME1 66 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A11_ME1 67 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A12_ME1 68 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A13_ME1 69 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A14_ME1 70 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A15_ME1 71 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A16_ME2 72 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A17_ME2 73 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A18_ME2 74 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A19_ME2 75 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A20_ME2 76 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A21_ME2 77 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A22_ME2 78 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A23_ME2 79 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A24_ME3 80 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A25_ME3 81 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A26_ME3 82 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A27_ME3 83 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A28_ME3 84 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A29_ME3 85 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A30_ME3 86 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_A31_ME3 87 /* ME_THREAD_A */ -#define IRQ_IXP23XX_ME_THREAD_B0_ME0 88 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B1_ME0 89 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B2_ME0 90 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B3_ME0 91 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B4_ME0 92 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B5_ME0 93 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B6_ME0 94 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B7_ME0 95 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B8_ME1 96 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B9_ME1 97 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B10_ME1 98 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B11_ME1 99 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B12_ME1 100 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B13_ME1 101 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B14_ME1 102 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B15_ME1 103 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B16_ME2 104 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B17_ME2 105 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B18_ME2 106 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B19_ME2 107 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B20_ME2 108 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B21_ME2 109 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B22_ME2 110 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B23_ME2 111 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B24_ME3 112 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B25_ME3 113 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B26_ME3 114 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B27_ME3 115 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B28_ME3 116 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B29_ME3 117 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B30_ME3 118 /* ME_THREAD_B */ -#define IRQ_IXP23XX_ME_THREAD_B31_ME3 119 /* ME_THREAD_B */ - -#define NUM_IXP23XX_RAW_IRQS 120 - -#define IRQ_IXP23XX_INTA 120 /* Indirect pcxg_pci_int_rph */ -#define IRQ_IXP23XX_INTB 121 /* Indirect pcxg_pci_int_rph */ - -#define NR_IXP23XX_IRQ (IRQ_IXP23XX_INTB + 1) - -/* - * We default to 32 per-board IRQs. Increase this number if you need - * more, but keep it realistic. - */ -#define NR_IXP23XX_MACH_IRQS 32 - -#define NR_IRQS (NR_IXP23XX_IRQS + NR_IXP23XX_MACH_IRQS) - -#define IXP23XX_MACH_IRQ(irq) (NR_IXP23XX_IRQ + (irq)) - - -/* - * IXDP2351-specific interrupts - */ - -/* - * External PCI interrupts signaled through INTB - * - */ -#define IXDP2351_INTB_IRQ_BASE 0 -#define IRQ_IXDP2351_INTA_82546 IXP23XX_MACH_IRQ(0) -#define IRQ_IXDP2351_INTB_82546 IXP23XX_MACH_IRQ(1) -#define IRQ_IXDP2351_SPCI_DB_0 IXP23XX_MACH_IRQ(2) -#define IRQ_IXDP2351_SPCI_DB_1 IXP23XX_MACH_IRQ(3) -#define IRQ_IXDP2351_SPCI_PMC_INTA IXP23XX_MACH_IRQ(4) -#define IRQ_IXDP2351_SPCI_PMC_INTB IXP23XX_MACH_IRQ(5) -#define IRQ_IXDP2351_SPCI_PMC_INTC IXP23XX_MACH_IRQ(6) -#define IRQ_IXDP2351_SPCI_PMC_INTD IXP23XX_MACH_IRQ(7) -#define IRQ_IXDP2351_SPCI_FIC IXP23XX_MACH_IRQ(8) - -#define IXDP2351_INTB_IRQ_BIT(irq) (irq - IXP23XX_MACH_IRQ(0)) -#define IXDP2351_INTB_IRQ_MASK(irq) (1 << IXDP2351_INTB_IRQ_BIT(irq)) -#define IXDP2351_INTB_IRQ_VALID 0x01FF -#define IXDP2351_INTB_IRQ_NUM 16 - -/* - * Other external interrupts signaled through INTA - */ -#define IXDP2351_INTA_IRQ_BASE 16 -#define IRQ_IXDP2351_IPMI_FROM IXP23XX_MACH_IRQ(16) -#define IRQ_IXDP2351_125US IXP23XX_MACH_IRQ(17) -#define IRQ_IXDP2351_DB_0_ADD IXP23XX_MACH_IRQ(18) -#define IRQ_IXDP2351_DB_1_ADD IXP23XX_MACH_IRQ(19) -#define IRQ_IXDP2351_DEBUG1 IXP23XX_MACH_IRQ(20) -#define IRQ_IXDP2351_ADD_UART IXP23XX_MACH_IRQ(21) -#define IRQ_IXDP2351_FIC_ADD IXP23XX_MACH_IRQ(24) -#define IRQ_IXDP2351_CS8900 IXP23XX_MACH_IRQ(25) -#define IRQ_IXDP2351_BBSRAM IXP23XX_MACH_IRQ(26) -#define IRQ_IXDP2351_CONFIG_MEDIA IXP23XX_MACH_IRQ(27) -#define IRQ_IXDP2351_CLOCK_REF IXP23XX_MACH_IRQ(28) -#define IRQ_IXDP2351_A10_NP IXP23XX_MACH_IRQ(29) -#define IRQ_IXDP2351_A11_NP IXP23XX_MACH_IRQ(30) -#define IRQ_IXDP2351_DEBUG_NP IXP23XX_MACH_IRQ(31) - -#define IXDP2351_INTA_IRQ_BIT(irq) (irq - IXP23XX_MACH_IRQ(16)) -#define IXDP2351_INTA_IRQ_MASK(irq) (1 << IXDP2351_INTA_IRQ_BIT(irq)) -#define IXDP2351_INTA_IRQ_VALID 0xFF3F -#define IXDP2351_INTA_IRQ_NUM 16 - - -/* - * ADI RoadRunner IRQs - */ -#define IRQ_ROADRUNNER_PCI_INTA IRQ_IXP23XX_INTA -#define IRQ_ROADRUNNER_PCI_INTB IRQ_IXP23XX_INTB -#define IRQ_ROADRUNNER_PCI_INTC IRQ_IXP23XX_GPIO11 -#define IRQ_ROADRUNNER_PCI_INTD IRQ_IXP23XX_GPIO12 - -/* - * Put new board definitions here - */ - - -#endif diff --git a/arch/arm/mach-ixp23xx/include/mach/ixdp2351.h b/arch/arm/mach-ixp23xx/include/mach/ixdp2351.h deleted file mode 100644 index 663951027de5..000000000000 --- a/arch/arm/mach-ixp23xx/include/mach/ixdp2351.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/include/mach/ixdp2351.h - * - * Register and other defines for IXDP2351 - * - * Copyright (c) 2002-2004 Intel Corp. - * Copytight (c) 2005 MontaVista Software, Inc. - * - * 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; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef __ASM_ARCH_IXDP2351_H -#define __ASM_ARCH_IXDP2351_H - -/* - * NP module memory map - */ -#define IXDP2351_NP_PHYS_BASE (IXP23XX_EXP_BUS_CS4_BASE) -#define IXDP2351_NP_PHYS_SIZE 0x00100000 -#define IXDP2351_NP_VIRT_BASE 0xeff00000 - -#define IXDP2351_VIRT_CS8900_BASE (IXDP2351_NP_VIRT_BASE) -#define IXDP2351_VIRT_CS8900_END (IXDP2351_VIRT_CS8900_BASE + 16) - -#define IXDP2351_VIRT_NP_CPLD_BASE (IXP23XX_EXP_BUS_CS4_BASE_VIRT + 0x00010000) - -#define IXDP2351_NP_CPLD_REG(reg) ((volatile u16 *)(IXDP2351_VIRT_NP_CPLD_BASE + reg)) - -#define IXDP2351_NP_CPLD_RESET1_REG IXDP2351_NP_CPLD_REG(0x00) -#define IXDP2351_NP_CPLD_LED_REG IXDP2351_NP_CPLD_REG(0x02) -#define IXDP2351_NP_CPLD_VERSION_REG IXDP2351_NP_CPLD_REG(0x04) - -/* - * Base board module memory map - */ - -#define IXDP2351_BB_BASE_PHYS (IXP23XX_EXP_BUS_CS5_BASE) -#define IXDP2351_BB_SIZE 0x01000000 -#define IXDP2351_BB_BASE_VIRT (0xee000000) - -#define IXDP2351_BB_AREA_BASE(offset) (IXDP2351_BB_BASE_VIRT + offset) - -#define IXDP2351_VIRT_NVRAM_BASE IXDP2351_BB_AREA_BASE(0x0) -#define IXDP2351_NVRAM_SIZE (0x20000) - -#define IXDP2351_VIRT_MB_IXF1104_BASE IXDP2351_BB_AREA_BASE(0x00020000) -#define IXDP2351_VIRT_ADD_UART_BASE IXDP2351_BB_AREA_BASE(0x000240C0) -#define IXDP2351_VIRT_FIC_BASE IXDP2351_BB_AREA_BASE(0x00200000) -#define IXDP2351_VIRT_DB0_BASE IXDP2351_BB_AREA_BASE(0x00400000) -#define IXDP2351_VIRT_DB1_BASE IXDP2351_BB_AREA_BASE(0x00600000) -#define IXDP2351_VIRT_CPLD_BASE IXDP2351_BB_AREA_BASE(0x00024000) - -/* - * On board CPLD registers - */ -#define IXDP2351_CPLD_BB_REG(reg) ((volatile u16 *)(IXDP2351_VIRT_CPLD_BASE + reg)) - -#define IXDP2351_CPLD_RESET0_REG IXDP2351_CPLD_BB_REG(0x00) -#define IXDP2351_CPLD_RESET1_REG IXDP2351_CPLD_BB_REG(0x04) - -#define IXDP2351_CPLD_RESET1_MAGIC 0x55AA -#define IXDP2351_CPLD_RESET1_ENABLE 0x8000 - -#define IXDP2351_CPLD_FPGA_CONFIG_REG IXDP2351_CPLD_BB_REG(0x08) -#define IXDP2351_CPLD_INTB_MASK_SET_REG IXDP2351_CPLD_BB_REG(0x10) -#define IXDP2351_CPLD_INTA_MASK_SET_REG IXDP2351_CPLD_BB_REG(0x14) -#define IXDP2351_CPLD_INTB_STAT_REG IXDP2351_CPLD_BB_REG(0x18) -#define IXDP2351_CPLD_INTA_STAT_REG IXDP2351_CPLD_BB_REG(0x1C) -#define IXDP2351_CPLD_INTB_RAW_REG IXDP2351_CPLD_BB_REG(0x20) /* read */ -#define IXDP2351_CPLD_INTA_RAW_REG IXDP2351_CPLD_BB_REG(0x24) /* read */ -#define IXDP2351_CPLD_INTB_MASK_CLR_REG IXDP2351_CPLD_INTB_RAW_REG /* write */ -#define IXDP2351_CPLD_INTA_MASK_CLR_REG IXDP2351_CPLD_INTA_RAW_REG /* write */ -#define IXDP2351_CPLD_INTB_SIM_REG IXDP2351_CPLD_BB_REG(0x28) -#define IXDP2351_CPLD_INTA_SIM_REG IXDP2351_CPLD_BB_REG(0x2C) - /* Interrupt bits are defined in irqs.h */ -#define IXDP2351_CPLD_BB_GBE0_REG IXDP2351_CPLD_BB_REG(0x30) -#define IXDP2351_CPLD_BB_GBE1_REG IXDP2351_CPLD_BB_REG(0x34) - -/* #define IXDP2351_CPLD_BB_MISC_REG IXDP2351_CPLD_REG(0x1C) */ -/* #define IXDP2351_CPLD_BB_MISC_REV_MASK 0xFF */ -/* #define IXDP2351_CPLD_BB_GDXCS0_REG IXDP2351_CPLD_REG(0x24) */ -/* #define IXDP2351_CPLD_BB_GDXCS1_REG IXDP2351_CPLD_REG(0x28) */ -/* #define IXDP2351_CPLD_BB_CLOCK_REG IXDP2351_CPLD_REG(0x04) */ - - -#endif diff --git a/arch/arm/mach-ixp23xx/include/mach/ixp23xx.h b/arch/arm/mach-ixp23xx/include/mach/ixp23xx.h deleted file mode 100644 index 6d02481b1d6d..000000000000 --- a/arch/arm/mach-ixp23xx/include/mach/ixp23xx.h +++ /dev/null @@ -1,298 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/include/mach/ixp23xx.h - * - * Register definitions for IXP23XX - * - * Copyright (C) 2003-2005 Intel Corporation. - * Copyright (C) 2005 MontaVista Software, Inc. - * - * Maintainer: Deepak Saxena - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __ASM_ARCH_IXP23XX_H -#define __ASM_ARCH_IXP23XX_H - -/* - * IXP2300 linux memory map: - * - * virt phys size - * fffd0000 a0000000 64K XSI2CPP_CSR - * fffc0000 c4000000 4K EXP_CFG - * fff00000 c8000000 64K PERIPHERAL - * fe000000 1c0000000 16M CAP_CSR - * fd000000 1c8000000 16M MSF_CSR - * fb000000 16M --- - * fa000000 1d8000000 32M PCI_IO - * f8000000 1da000000 32M PCI_CFG - * f6000000 1de000000 32M PCI_CREG - * f4000000 32M --- - * f0000000 1e0000000 64M PCI_MEM - * e[c-f]000000 per-platform mappings - */ - - -/**************************************************************************** - * Static mappings. - ****************************************************************************/ -#define IXP23XX_XSI2CPP_CSR_PHYS 0xa0000000 -#define IXP23XX_XSI2CPP_CSR_VIRT 0xfffd0000 -#define IXP23XX_XSI2CPP_CSR_SIZE 0x00010000 - -#define IXP23XX_EXP_CFG_PHYS 0xc4000000 -#define IXP23XX_EXP_CFG_VIRT 0xfffc0000 -#define IXP23XX_EXP_CFG_SIZE 0x00001000 - -#define IXP23XX_PERIPHERAL_PHYS 0xc8000000 -#define IXP23XX_PERIPHERAL_VIRT 0xfff00000 -#define IXP23XX_PERIPHERAL_SIZE 0x00010000 - -#define IXP23XX_CAP_CSR_PHYS 0x1c0000000ULL -#define IXP23XX_CAP_CSR_VIRT 0xfe000000 -#define IXP23XX_CAP_CSR_SIZE 0x01000000 - -#define IXP23XX_MSF_CSR_PHYS 0x1c8000000ULL -#define IXP23XX_MSF_CSR_VIRT 0xfd000000 -#define IXP23XX_MSF_CSR_SIZE 0x01000000 - -#define IXP23XX_PCI_IO_PHYS 0x1d8000000ULL -#define IXP23XX_PCI_IO_VIRT 0xfa000000 -#define IXP23XX_PCI_IO_SIZE 0x02000000 - -#define IXP23XX_PCI_CFG_PHYS 0x1da000000ULL -#define IXP23XX_PCI_CFG_VIRT 0xf8000000 -#define IXP23XX_PCI_CFG_SIZE 0x02000000 -#define IXP23XX_PCI_CFG0_VIRT IXP23XX_PCI_CFG_VIRT -#define IXP23XX_PCI_CFG1_VIRT (IXP23XX_PCI_CFG_VIRT + 0x01000000) - -#define IXP23XX_PCI_CREG_PHYS 0x1de000000ULL -#define IXP23XX_PCI_CREG_VIRT 0xf6000000 -#define IXP23XX_PCI_CREG_SIZE 0x02000000 -#define IXP23XX_PCI_CSR_VIRT (IXP23XX_PCI_CREG_VIRT + 0x01000000) - -#define IXP23XX_PCI_MEM_START 0xe0000000 -#define IXP23XX_PCI_MEM_PHYS 0x1e0000000ULL -#define IXP23XX_PCI_MEM_VIRT 0xf0000000 -#define IXP23XX_PCI_MEM_SIZE 0x04000000 - - -/**************************************************************************** - * XSI2CPP CSRs. - ****************************************************************************/ -#define IXP23XX_XSI2CPP_REG(x) ((volatile unsigned long *)(IXP23XX_XSI2CPP_CSR_VIRT + (x))) -#define IXP23XX_CPP2XSI_CURR_XFER_REG3 IXP23XX_XSI2CPP_REG(0xf8) -#define IXP23XX_CPP2XSI_ADDR_31 (1 << 19) -#define IXP23XX_CPP2XSI_PSH_OFF (1 << 20) -#define IXP23XX_CPP2XSI_COH_OFF (1 << 21) - - -/**************************************************************************** - * Expansion Bus Config. - ****************************************************************************/ -#define IXP23XX_EXP_CFG_REG(x) ((volatile unsigned long *)(IXP23XX_EXP_CFG_VIRT + (x))) -#define IXP23XX_EXP_CS0 IXP23XX_EXP_CFG_REG(0x00) -#define IXP23XX_EXP_CS1 IXP23XX_EXP_CFG_REG(0x04) -#define IXP23XX_EXP_CS2 IXP23XX_EXP_CFG_REG(0x08) -#define IXP23XX_EXP_CS3 IXP23XX_EXP_CFG_REG(0x0c) -#define IXP23XX_EXP_CS4 IXP23XX_EXP_CFG_REG(0x10) -#define IXP23XX_EXP_CS5 IXP23XX_EXP_CFG_REG(0x14) -#define IXP23XX_EXP_CS6 IXP23XX_EXP_CFG_REG(0x18) -#define IXP23XX_EXP_CS7 IXP23XX_EXP_CFG_REG(0x1c) -#define IXP23XX_FLASH_WRITABLE (0x2) -#define IXP23XX_FLASH_BUS8 (0x1) - -#define IXP23XX_EXP_CFG0 IXP23XX_EXP_CFG_REG(0x20) -#define IXP23XX_EXP_CFG1 IXP23XX_EXP_CFG_REG(0x24) -#define IXP23XX_EXP_CFG0_MEM_MAP (1 << 31) -#define IXP23XX_EXP_CFG0_XSCALE_SPEED_SEL (3 << 22) -#define IXP23XX_EXP_CFG0_XSCALE_SPEED_EN (1 << 21) -#define IXP23XX_EXP_CFG0_CPP_SPEED_SEL (3 << 19) -#define IXP23XX_EXP_CFG0_CPP_SPEED_EN (1 << 18) -#define IXP23XX_EXP_CFG0_PCI_SWIN (3 << 16) -#define IXP23XX_EXP_CFG0_PCI_DWIN (3 << 14) -#define IXP23XX_EXP_CFG0_PCI33_MODE (1 << 13) -#define IXP23XX_EXP_CFG0_QDR_SPEED_SEL (1 << 12) -#define IXP23XX_EXP_CFG0_CPP_DIV_SEL (1 << 5) -#define IXP23XX_EXP_CFG0_XSI_NOT_PRES (1 << 4) -#define IXP23XX_EXP_CFG0_PROM_BOOT (1 << 3) -#define IXP23XX_EXP_CFG0_PCI_ARB (1 << 2) -#define IXP23XX_EXP_CFG0_PCI_HOST (1 << 1) -#define IXP23XX_EXP_CFG0_FLASH_WIDTH (1 << 0) - -#define IXP23XX_EXP_UNIT_FUSE IXP23XX_EXP_CFG_REG(0x28) -#define IXP23XX_EXP_MSF_MUX IXP23XX_EXP_CFG_REG(0x30) -#define IXP23XX_EXP_CFG_FUSE IXP23XX_EXP_CFG_REG(0x34) - -#define IXP23XX_EXP_BUS_PHYS 0x90000000 -#define IXP23XX_EXP_BUS_WINDOW_SIZE 0x01000000 - -#define IXP23XX_EXP_BUS_CS0_BASE (IXP23XX_EXP_BUS_PHYS + 0x00000000) -#define IXP23XX_EXP_BUS_CS1_BASE (IXP23XX_EXP_BUS_PHYS + 0x01000000) -#define IXP23XX_EXP_BUS_CS2_BASE (IXP23XX_EXP_BUS_PHYS + 0x02000000) -#define IXP23XX_EXP_BUS_CS3_BASE (IXP23XX_EXP_BUS_PHYS + 0x03000000) -#define IXP23XX_EXP_BUS_CS4_BASE (IXP23XX_EXP_BUS_PHYS + 0x04000000) -#define IXP23XX_EXP_BUS_CS5_BASE (IXP23XX_EXP_BUS_PHYS + 0x05000000) -#define IXP23XX_EXP_BUS_CS6_BASE (IXP23XX_EXP_BUS_PHYS + 0x06000000) -#define IXP23XX_EXP_BUS_CS7_BASE (IXP23XX_EXP_BUS_PHYS + 0x07000000) - - -/**************************************************************************** - * Peripherals. - ****************************************************************************/ -#define IXP23XX_UART1_VIRT (IXP23XX_PERIPHERAL_VIRT + 0x0000) -#define IXP23XX_UART2_VIRT (IXP23XX_PERIPHERAL_VIRT + 0x1000) -#define IXP23XX_PMU_VIRT (IXP23XX_PERIPHERAL_VIRT + 0x2000) -#define IXP23XX_INTC_VIRT (IXP23XX_PERIPHERAL_VIRT + 0x3000) -#define IXP23XX_GPIO_VIRT (IXP23XX_PERIPHERAL_VIRT + 0x4000) -#define IXP23XX_TIMER_VIRT (IXP23XX_PERIPHERAL_VIRT + 0x5000) -#define IXP23XX_NPE0_VIRT (IXP23XX_PERIPHERAL_VIRT + 0x6000) -#define IXP23XX_DSR_VIRT (IXP23XX_PERIPHERAL_VIRT + 0x7000) -#define IXP23XX_NPE1_VIRT (IXP23XX_PERIPHERAL_VIRT + 0x8000) -#define IXP23XX_ETH0_VIRT (IXP23XX_PERIPHERAL_VIRT + 0x9000) -#define IXP23XX_ETH1_VIRT (IXP23XX_PERIPHERAL_VIRT + 0xA000) -#define IXP23XX_GIG0_VIRT (IXP23XX_PERIPHERAL_VIRT + 0xB000) -#define IXP23XX_GIG1_VIRT (IXP23XX_PERIPHERAL_VIRT + 0xC000) -#define IXP23XX_DDRS_VIRT (IXP23XX_PERIPHERAL_VIRT + 0xD000) - -#define IXP23XX_UART1_PHYS (IXP23XX_PERIPHERAL_PHYS + 0x0000) -#define IXP23XX_UART2_PHYS (IXP23XX_PERIPHERAL_PHYS + 0x1000) -#define IXP23XX_PMU_PHYS (IXP23XX_PERIPHERAL_PHYS + 0x2000) -#define IXP23XX_INTC_PHYS (IXP23XX_PERIPHERAL_PHYS + 0x3000) -#define IXP23XX_GPIO_PHYS (IXP23XX_PERIPHERAL_PHYS + 0x4000) -#define IXP23XX_TIMER_PHYS (IXP23XX_PERIPHERAL_PHYS + 0x5000) -#define IXP23XX_NPE0_PHYS (IXP23XX_PERIPHERAL_PHYS + 0x6000) -#define IXP23XX_DSR_PHYS (IXP23XX_PERIPHERAL_PHYS + 0x7000) -#define IXP23XX_NPE1_PHYS (IXP23XX_PERIPHERAL_PHYS + 0x8000) -#define IXP23XX_ETH0_PHYS (IXP23XX_PERIPHERAL_PHYS + 0x9000) -#define IXP23XX_ETH1_PHYS (IXP23XX_PERIPHERAL_PHYS + 0xA000) -#define IXP23XX_GIG0_PHYS (IXP23XX_PERIPHERAL_PHYS + 0xB000) -#define IXP23XX_GIG1_PHYS (IXP23XX_PERIPHERAL_PHYS + 0xC000) -#define IXP23XX_DDRS_PHYS (IXP23XX_PERIPHERAL_PHYS + 0xD000) - - -/**************************************************************************** - * Interrupt controller. - ****************************************************************************/ -#define IXP23XX_INTC_REG(x) ((volatile unsigned long *)(IXP23XX_INTC_VIRT + (x))) -#define IXP23XX_INTR_ST1 IXP23XX_INTC_REG(0x00) -#define IXP23XX_INTR_ST2 IXP23XX_INTC_REG(0x04) -#define IXP23XX_INTR_ST3 IXP23XX_INTC_REG(0x08) -#define IXP23XX_INTR_ST4 IXP23XX_INTC_REG(0x0c) -#define IXP23XX_INTR_EN1 IXP23XX_INTC_REG(0x10) -#define IXP23XX_INTR_EN2 IXP23XX_INTC_REG(0x14) -#define IXP23XX_INTR_EN3 IXP23XX_INTC_REG(0x18) -#define IXP23XX_INTR_EN4 IXP23XX_INTC_REG(0x1c) -#define IXP23XX_INTR_SEL1 IXP23XX_INTC_REG(0x20) -#define IXP23XX_INTR_SEL2 IXP23XX_INTC_REG(0x24) -#define IXP23XX_INTR_SEL3 IXP23XX_INTC_REG(0x28) -#define IXP23XX_INTR_SEL4 IXP23XX_INTC_REG(0x2c) -#define IXP23XX_INTR_IRQ_ST1 IXP23XX_INTC_REG(0x30) -#define IXP23XX_INTR_IRQ_ST2 IXP23XX_INTC_REG(0x34) -#define IXP23XX_INTR_IRQ_ST3 IXP23XX_INTC_REG(0x38) -#define IXP23XX_INTR_IRQ_ST4 IXP23XX_INTC_REG(0x3c) -#define IXP23XX_INTR_IRQ_ENC_ST_OFFSET 0x54 - - -/**************************************************************************** - * GPIO. - ****************************************************************************/ -#define IXP23XX_GPIO_REG(x) ((volatile unsigned long *)(IXP23XX_GPIO_VIRT + (x))) -#define IXP23XX_GPIO_GPOUTR IXP23XX_GPIO_REG(0x00) -#define IXP23XX_GPIO_GPOER IXP23XX_GPIO_REG(0x04) -#define IXP23XX_GPIO_GPINR IXP23XX_GPIO_REG(0x08) -#define IXP23XX_GPIO_GPISR IXP23XX_GPIO_REG(0x0c) -#define IXP23XX_GPIO_GPIT1R IXP23XX_GPIO_REG(0x10) -#define IXP23XX_GPIO_GPIT2R IXP23XX_GPIO_REG(0x14) -#define IXP23XX_GPIO_GPCLKR IXP23XX_GPIO_REG(0x18) -#define IXP23XX_GPIO_GPDBSELR IXP23XX_GPIO_REG(0x1c) - -#define IXP23XX_GPIO_STYLE_MASK 0x7 -#define IXP23XX_GPIO_STYLE_ACTIVE_HIGH 0x0 -#define IXP23XX_GPIO_STYLE_ACTIVE_LOW 0x1 -#define IXP23XX_GPIO_STYLE_RISING_EDGE 0x2 -#define IXP23XX_GPIO_STYLE_FALLING_EDGE 0x3 -#define IXP23XX_GPIO_STYLE_TRANSITIONAL 0x4 - -#define IXP23XX_GPIO_STYLE_SIZE 3 - - -/**************************************************************************** - * Timer. - ****************************************************************************/ -#define IXP23XX_TIMER_REG(x) ((volatile unsigned long *)(IXP23XX_TIMER_VIRT + (x))) -#define IXP23XX_TIMER_CONT IXP23XX_TIMER_REG(0x00) -#define IXP23XX_TIMER1_TIMESTAMP IXP23XX_TIMER_REG(0x04) -#define IXP23XX_TIMER1_RELOAD IXP23XX_TIMER_REG(0x08) -#define IXP23XX_TIMER2_TIMESTAMP IXP23XX_TIMER_REG(0x0c) -#define IXP23XX_TIMER2_RELOAD IXP23XX_TIMER_REG(0x10) -#define IXP23XX_TIMER_WDOG IXP23XX_TIMER_REG(0x14) -#define IXP23XX_TIMER_WDOG_EN IXP23XX_TIMER_REG(0x18) -#define IXP23XX_TIMER_WDOG_KEY IXP23XX_TIMER_REG(0x1c) -#define IXP23XX_TIMER_WDOG_KEY_MAGIC 0x482e -#define IXP23XX_TIMER_STATUS IXP23XX_TIMER_REG(0x20) -#define IXP23XX_TIMER_SOFT_RESET IXP23XX_TIMER_REG(0x24) -#define IXP23XX_TIMER_SOFT_RESET_EN IXP23XX_TIMER_REG(0x28) - -#define IXP23XX_TIMER_ENABLE (1 << 0) -#define IXP23XX_TIMER_ONE_SHOT (1 << 1) -/* Low order bits of reload value ignored */ -#define IXP23XX_TIMER_RELOAD_MASK (0x3) -#define IXP23XX_TIMER_DISABLED (0x0) -#define IXP23XX_TIMER1_INT_PEND (1 << 0) -#define IXP23XX_TIMER2_INT_PEND (1 << 1) -#define IXP23XX_TIMER_STATUS_TS_PEND (1 << 2) -#define IXP23XX_TIMER_STATUS_WDOG_PEND (1 << 3) -#define IXP23XX_TIMER_STATUS_WARM_RESET (1 << 4) - - -/**************************************************************************** - * CAP CSRs. - ****************************************************************************/ -#define IXP23XX_GLOBAL_REG(x) ((volatile unsigned long *)(IXP23XX_CAP_CSR_VIRT + 0x4a00 + (x))) -#define IXP23XX_PRODUCT_ID IXP23XX_GLOBAL_REG(0x00) -#define IXP23XX_MISC_CONTROL IXP23XX_GLOBAL_REG(0x04) -#define IXP23XX_MSF_CLK_CNTRL IXP23XX_GLOBAL_REG(0x08) -#define IXP23XX_RESET0 IXP23XX_GLOBAL_REG(0x0c) -#define IXP23XX_RESET1 IXP23XX_GLOBAL_REG(0x10) -#define IXP23XX_STRAP_OPTIONS IXP23XX_GLOBAL_REG(0x18) - -#define IXP23XX_ENABLE_WATCHDOG (1 << 24) -#define IXP23XX_SHPC_INIT_COMP (1 << 21) -#define IXP23XX_RST_ALL (1 << 16) -#define IXP23XX_RESET_PCI (1 << 2) -#define IXP23XX_PCI_UNIT_RESET (1 << 1) -#define IXP23XX_XSCALE_RESET (1 << 0) - -#define IXP23XX_UENGINE_CSR_VIRT_BASE (IXP23XX_CAP_CSR_VIRT + 0x18000) - - -/**************************************************************************** - * PCI CSRs. - ****************************************************************************/ -#define IXP23XX_PCI_CREG(x) ((volatile unsigned long *)(IXP23XX_PCI_CREG_VIRT + (x))) -#define IXP23XX_PCI_CMDSTAT IXP23XX_PCI_CREG(0x04) -#define IXP23XX_PCI_SRAM_BAR IXP23XX_PCI_CREG(0x14) -#define IXP23XX_PCI_SDRAM_BAR IXP23XX_PCI_CREG(0x18) - - -#define IXP23XX_PCI_CSR(x) ((volatile unsigned long *)(IXP23XX_PCI_CREG_VIRT + 0x01000000 + (x))) -#define IXP23XX_PCI_OUT_INT_STATUS IXP23XX_PCI_CSR(0x0030) -#define IXP23XX_PCI_OUT_INT_MASK IXP23XX_PCI_CSR(0x0034) -#define IXP23XX_PCI_SRAM_BASE_ADDR_MASK IXP23XX_PCI_CSR(0x00fc) -#define IXP23XX_PCI_DRAM_BASE_ADDR_MASK IXP23XX_PCI_CSR(0x0100) -#define IXP23XX_PCI_CONTROL IXP23XX_PCI_CSR(0x013c) -#define IXP23XX_PCI_ADDR_EXT IXP23XX_PCI_CSR(0x0140) -#define IXP23XX_PCI_ME_PUSH_STATUS IXP23XX_PCI_CSR(0x0148) -#define IXP23XX_PCI_ME_PUSH_EN IXP23XX_PCI_CSR(0x014c) -#define IXP23XX_PCI_ERR_STATUS IXP23XX_PCI_CSR(0x0150) -#define IXP23XX_PCI_ERROR_STATUS IXP23XX_PCI_CSR(0x0150) -#define IXP23XX_PCI_ERR_ENABLE IXP23XX_PCI_CSR(0x0154) -#define IXP23XX_PCI_XSCALE_INT_STATUS IXP23XX_PCI_CSR(0x0158) -#define IXP23XX_PCI_XSCALE_INT_ENABLE IXP23XX_PCI_CSR(0x015c) -#define IXP23XX_PCI_CPP_ADDR_BITS IXP23XX_PCI_CSR(0x0160) - - -#endif diff --git a/arch/arm/mach-ixp23xx/include/mach/memory.h b/arch/arm/mach-ixp23xx/include/mach/memory.h deleted file mode 100644 index 6cf0704e946a..000000000000 --- a/arch/arm/mach-ixp23xx/include/mach/memory.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/include/mach/memory.h - * - * Copyright (c) 2003-2004 Intel 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; either version 2 of the License, or (at your - * option) any later version. - */ - -#ifndef __ASM_ARCH_MEMORY_H -#define __ASM_ARCH_MEMORY_H - -#include - -/* - * Physical DRAM offset. - */ -#define PLAT_PHYS_OFFSET (0x00000000) - -#define IXP23XX_PCI_SDRAM_OFFSET (*((volatile int *)IXP23XX_PCI_SDRAM_BAR) & 0xfffffff0) - -#define __phys_to_bus(x) ((x) + (IXP23XX_PCI_SDRAM_OFFSET - PHYS_OFFSET)) -#define __bus_to_phys(x) ((x) - (IXP23XX_PCI_SDRAM_OFFSET - PHYS_OFFSET)) - -#define __virt_to_bus(v) __phys_to_bus(__virt_to_phys(v)) -#define __bus_to_virt(b) __phys_to_virt(__bus_to_phys(b)) -#define __pfn_to_bus(p) __phys_to_bus(__pfn_to_phys(p)) -#define __bus_to_pfn(b) __phys_to_pfn(__bus_to_phys(b)) - -#define arch_is_coherent() 1 - -#endif diff --git a/arch/arm/mach-ixp23xx/include/mach/platform.h b/arch/arm/mach-ixp23xx/include/mach/platform.h deleted file mode 100644 index 50de558e722e..000000000000 --- a/arch/arm/mach-ixp23xx/include/mach/platform.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/include/mach/platform.h - * - * Various bits of code used by platform-level code. - * - * Author: Deepak Saxena - * - * Copyright 2005 (c) MontaVista Software, Inc. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#ifndef __ASSEMBLY__ - -static inline unsigned long ixp2000_reg_read(volatile void *reg) -{ - return *((volatile unsigned long *)reg); -} - -static inline void ixp2000_reg_write(volatile void *reg, unsigned long val) -{ - *((volatile unsigned long *)reg) = val; -} - -static inline void ixp2000_reg_wrb(volatile void *reg, unsigned long val) -{ - *((volatile unsigned long *)reg) = val; -} - -struct pci_sys_data; - -void ixp23xx_map_io(void); -void ixp23xx_init_irq(void); -void ixp23xx_sys_init(void); -void ixp23xx_restart(char, const char *); -int ixp23xx_pci_setup(int, struct pci_sys_data *); -void ixp23xx_pci_preinit(void); -struct pci_bus *ixp23xx_pci_scan_bus(int, struct pci_sys_data*); -void ixp23xx_pci_slave_init(void); - -extern struct sys_timer ixp23xx_timer; - -#define IXP23XX_UART_XTAL 14745600 - -#ifndef __ASSEMBLY__ -/* - * Is system memory on the XSI or CPP bus? - */ -static inline unsigned ixp23xx_cpp_boot(void) -{ - return (*IXP23XX_EXP_CFG0 & IXP23XX_EXP_CFG0_XSI_NOT_PRES); -} -#endif - - -#endif diff --git a/arch/arm/mach-ixp23xx/include/mach/time.h b/arch/arm/mach-ixp23xx/include/mach/time.h deleted file mode 100644 index b61dafc884ac..000000000000 --- a/arch/arm/mach-ixp23xx/include/mach/time.h +++ /dev/null @@ -1,3 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/include/mach/time.h - */ diff --git a/arch/arm/mach-ixp23xx/include/mach/timex.h b/arch/arm/mach-ixp23xx/include/mach/timex.h deleted file mode 100644 index e341e9cf9c37..000000000000 --- a/arch/arm/mach-ixp23xx/include/mach/timex.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/include/mach/timex.h - * - * XScale architecture timex specifications - */ - -#define CLOCK_TICK_RATE 75000000 diff --git a/arch/arm/mach-ixp23xx/include/mach/uncompress.h b/arch/arm/mach-ixp23xx/include/mach/uncompress.h deleted file mode 100644 index 8b4c358d2c04..000000000000 --- a/arch/arm/mach-ixp23xx/include/mach/uncompress.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/include/mach/uncompress.h - * - * Copyright (C) 2002-2004 Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __ASM_ARCH_UNCOMPRESS_H -#define __ASM_ARCH_UNCOMPRESS_H - -#include -#include - -#define UART_BASE ((volatile u32 *)IXP23XX_UART1_PHYS) - -static inline void putc(char c) -{ - int j; - - for (j = 0; j < 0x1000; j++) { - if (UART_BASE[UART_LSR] & UART_LSR_THRE) - break; - barrier(); - } - - UART_BASE[UART_TX] = c; -} - -static inline void flush(void) -{ -} - -#define arch_decomp_setup() -#define arch_decomp_wdog() - - -#endif diff --git a/arch/arm/mach-ixp23xx/ixdp2351.c b/arch/arm/mach-ixp23xx/ixdp2351.c deleted file mode 100644 index b0e07db5ceaf..000000000000 --- a/arch/arm/mach-ixp23xx/ixdp2351.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/ixdp2351.c - * - * IXDP2351 board-specific routines - * - * Author: Deepak Saxena - * - * Copyright 2005 (c) MontaVista Software, Inc. - * - * Based on 2.4 code Copyright 2004 (c) Intel Corporation - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* - * IXDP2351 Interrupt Handling - */ -static void ixdp2351_inta_mask(struct irq_data *d) -{ - *IXDP2351_CPLD_INTA_MASK_SET_REG = IXDP2351_INTA_IRQ_MASK(d->irq); -} - -static void ixdp2351_inta_unmask(struct irq_data *d) -{ - *IXDP2351_CPLD_INTA_MASK_CLR_REG = IXDP2351_INTA_IRQ_MASK(d->irq); -} - -static void ixdp2351_inta_handler(unsigned int irq, struct irq_desc *desc) -{ - u16 ex_interrupt = - *IXDP2351_CPLD_INTA_STAT_REG & IXDP2351_INTA_IRQ_VALID; - int i; - - desc->irq_data.chip->irq_mask(&desc->irq_data); - - for (i = 0; i < IXDP2351_INTA_IRQ_NUM; i++) { - if (ex_interrupt & (1 << i)) { - int cpld_irq = - IXP23XX_MACH_IRQ(IXDP2351_INTA_IRQ_BASE + i); - generic_handle_irq(cpld_irq); - } - } - - desc->irq_data.chip->irq_unmask(&desc->irq_data); -} - -static struct irq_chip ixdp2351_inta_chip = { - .irq_ack = ixdp2351_inta_mask, - .irq_mask = ixdp2351_inta_mask, - .irq_unmask = ixdp2351_inta_unmask -}; - -static void ixdp2351_intb_mask(struct irq_data *d) -{ - *IXDP2351_CPLD_INTB_MASK_SET_REG = IXDP2351_INTB_IRQ_MASK(d->irq); -} - -static void ixdp2351_intb_unmask(struct irq_data *d) -{ - *IXDP2351_CPLD_INTB_MASK_CLR_REG = IXDP2351_INTB_IRQ_MASK(d->irq); -} - -static void ixdp2351_intb_handler(unsigned int irq, struct irq_desc *desc) -{ - u16 ex_interrupt = - *IXDP2351_CPLD_INTB_STAT_REG & IXDP2351_INTB_IRQ_VALID; - int i; - - desc->irq_data.chip->irq_ack(&desc->irq_data); - - for (i = 0; i < IXDP2351_INTB_IRQ_NUM; i++) { - if (ex_interrupt & (1 << i)) { - int cpld_irq = - IXP23XX_MACH_IRQ(IXDP2351_INTB_IRQ_BASE + i); - generic_handle_irq(cpld_irq); - } - } - - desc->irq_data.chip->irq_unmask(&desc->irq_data); -} - -static struct irq_chip ixdp2351_intb_chip = { - .irq_ack = ixdp2351_intb_mask, - .irq_mask = ixdp2351_intb_mask, - .irq_unmask = ixdp2351_intb_unmask -}; - -void __init ixdp2351_init_irq(void) -{ - int irq; - - /* Mask all interrupts from CPLD, disable simulation */ - *IXDP2351_CPLD_INTA_MASK_SET_REG = (u16) -1; - *IXDP2351_CPLD_INTB_MASK_SET_REG = (u16) -1; - *IXDP2351_CPLD_INTA_SIM_REG = 0; - *IXDP2351_CPLD_INTB_SIM_REG = 0; - - ixp23xx_init_irq(); - - for (irq = IXP23XX_MACH_IRQ(IXDP2351_INTA_IRQ_BASE); - irq < - IXP23XX_MACH_IRQ(IXDP2351_INTA_IRQ_BASE + IXDP2351_INTA_IRQ_NUM); - irq++) { - if (IXDP2351_INTA_IRQ_MASK(irq) & IXDP2351_INTA_IRQ_VALID) { - set_irq_flags(irq, IRQF_VALID); - irq_set_chip_and_handler(irq, &ixdp2351_inta_chip, - handle_level_irq); - } - } - - for (irq = IXP23XX_MACH_IRQ(IXDP2351_INTB_IRQ_BASE); - irq < - IXP23XX_MACH_IRQ(IXDP2351_INTB_IRQ_BASE + IXDP2351_INTB_IRQ_NUM); - irq++) { - if (IXDP2351_INTB_IRQ_MASK(irq) & IXDP2351_INTB_IRQ_VALID) { - set_irq_flags(irq, IRQF_VALID); - irq_set_chip_and_handler(irq, &ixdp2351_intb_chip, - handle_level_irq); - } - } - - irq_set_chained_handler(IRQ_IXP23XX_INTA, ixdp2351_inta_handler); - irq_set_chained_handler(IRQ_IXP23XX_INTB, ixdp2351_intb_handler); -} - -/* - * IXDP2351 PCI - */ - -/* - * This board does not do normal PCI IRQ routing, or any - * sort of swizzling, so we just need to check where on the - * bus the device is and figure out what CPLD pin it is - * being routed to. - */ -#define DEVPIN(dev, pin) ((pin) | ((dev) << 3)) - -static int __init ixdp2351_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) -{ - u8 bus = dev->bus->number; - u32 devpin = DEVPIN(PCI_SLOT(dev->devfn), pin); - struct pci_bus *tmp_bus = dev->bus; - - /* Primary bus, no interrupts here */ - if (!bus) - return -1; - - /* Lookup first leaf in bus tree */ - while ((tmp_bus->parent != NULL) && (tmp_bus->parent->parent != NULL)) - tmp_bus = tmp_bus->parent; - - /* Select between known bridges */ - switch (tmp_bus->self->devfn | (tmp_bus->self->bus->number << 8)) { - /* Device is located after first bridge */ - case 0x0008: - if (tmp_bus == dev->bus) { - /* Device is located directy after first bridge */ - switch (devpin) { - /* Onboard 82546 */ - case DEVPIN(1, 1): /* Onboard 82546 ch 0 */ - return IRQ_IXDP2351_INTA_82546; - case DEVPIN(1, 2): /* Onboard 82546 ch 1 */ - return IRQ_IXDP2351_INTB_82546; - /* PMC SLOT */ - case DEVPIN(0, 1): /* PMCP INTA# */ - case DEVPIN(2, 4): /* PMCS INTD# */ - return IRQ_IXDP2351_SPCI_PMC_INTA; - case DEVPIN(0, 2): /* PMCP INTB# */ - case DEVPIN(2, 1): /* PMCS INTA# */ - return IRQ_IXDP2351_SPCI_PMC_INTB; - case DEVPIN(0, 3): /* PMCP INTC# */ - case DEVPIN(2, 2): /* PMCS INTB# */ - return IRQ_IXDP2351_SPCI_PMC_INTC; - case DEVPIN(0, 4): /* PMCP INTD# */ - case DEVPIN(2, 3): /* PMCS INTC# */ - return IRQ_IXDP2351_SPCI_PMC_INTD; - } - } else { - /* Device is located indirectly after first bridge */ - /* Not supported now */ - return -1; - } - break; - case 0x0010: - if (tmp_bus == dev->bus) { - /* Device is located directy after second bridge */ - /* Secondary bus of second bridge */ - switch (devpin) { - case DEVPIN(0, 1): /* DB#0 */ - case DEVPIN(0, 2): - case DEVPIN(0, 3): - case DEVPIN(0, 4): - return IRQ_IXDP2351_SPCI_DB_0; - case DEVPIN(1, 1): /* DB#1 */ - case DEVPIN(1, 2): - case DEVPIN(1, 3): - case DEVPIN(1, 4): - return IRQ_IXDP2351_SPCI_DB_1; - case DEVPIN(2, 1): /* FIC1 */ - case DEVPIN(2, 2): - case DEVPIN(2, 3): - case DEVPIN(2, 4): - case DEVPIN(3, 1): /* FIC2 */ - case DEVPIN(3, 2): - case DEVPIN(3, 3): - case DEVPIN(3, 4): - return IRQ_IXDP2351_SPCI_FIC; - } - } else { - /* Device is located indirectly after second bridge */ - /* Not supported now */ - return -1; - } - break; - } - - return -1; -} - -struct hw_pci ixdp2351_pci __initdata = { - .nr_controllers = 1, - .preinit = ixp23xx_pci_preinit, - .setup = ixp23xx_pci_setup, - .scan = ixp23xx_pci_scan_bus, - .map_irq = ixdp2351_map_irq, -}; - -int __init ixdp2351_pci_init(void) -{ - if (machine_is_ixdp2351()) - pci_common_init(&ixdp2351_pci); - - return 0; -} - -subsys_initcall(ixdp2351_pci_init); - -/* - * IXDP2351 Static Mapped I/O - */ -static struct map_desc ixdp2351_io_desc[] __initdata = { - { - .virtual = IXDP2351_NP_VIRT_BASE, - .pfn = __phys_to_pfn((u64)IXDP2351_NP_PHYS_BASE), - .length = IXDP2351_NP_PHYS_SIZE, - .type = MT_DEVICE - }, { - .virtual = IXDP2351_BB_BASE_VIRT, - .pfn = __phys_to_pfn((u64)IXDP2351_BB_BASE_PHYS), - .length = IXDP2351_BB_SIZE, - .type = MT_DEVICE - } -}; - -static void __init ixdp2351_map_io(void) -{ - ixp23xx_map_io(); - iotable_init(ixdp2351_io_desc, ARRAY_SIZE(ixdp2351_io_desc)); -} - -static struct physmap_flash_data ixdp2351_flash_data = { - .width = 1, -}; - -static struct resource ixdp2351_flash_resource = { - .start = 0x90000000, - .end = 0x93ffffff, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device ixdp2351_flash = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &ixdp2351_flash_data, - }, - .num_resources = 1, - .resource = &ixdp2351_flash_resource, -}; - -static void __init ixdp2351_init(void) -{ - platform_device_register(&ixdp2351_flash); - - /* - * Mark flash as writeable - */ - IXP23XX_EXP_CS0[0] |= IXP23XX_FLASH_WRITABLE; - IXP23XX_EXP_CS0[1] |= IXP23XX_FLASH_WRITABLE; - IXP23XX_EXP_CS0[2] |= IXP23XX_FLASH_WRITABLE; - IXP23XX_EXP_CS0[3] |= IXP23XX_FLASH_WRITABLE; - - ixp23xx_sys_init(); -} - -static void ixdp2351_restart(char mode, const char *cmd) -{ - /* First try machine specific support */ - - *IXDP2351_CPLD_RESET1_REG = IXDP2351_CPLD_RESET1_MAGIC; - (void) *IXDP2351_CPLD_RESET1_REG; - *IXDP2351_CPLD_RESET1_REG = IXDP2351_CPLD_RESET1_ENABLE; - - ixp23xx_restart(mode, cmd); -} - -MACHINE_START(IXDP2351, "Intel IXDP2351 Development Platform") - /* Maintainer: MontaVista Software, Inc. */ - .map_io = ixdp2351_map_io, - .init_irq = ixdp2351_init_irq, - .timer = &ixp23xx_timer, - .atag_offset = 0x100, - .init_machine = ixdp2351_init, - .restart = ixdp2351_restart, -MACHINE_END diff --git a/arch/arm/mach-ixp23xx/pci.c b/arch/arm/mach-ixp23xx/pci.c deleted file mode 100644 index 911f5a58e006..000000000000 --- a/arch/arm/mach-ixp23xx/pci.c +++ /dev/null @@ -1,294 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/pci.c - * - * PCI routines for IXP23XX based systems - * - * Copyright (c) 2005 MontaVista Software, Inc. - * - * based on original code: - * - * Author: Naeem Afzal - * Copyright 2002-2005 Intel 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; either version 2 of the License, or (at your - * option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -extern int (*external_fault) (unsigned long, struct pt_regs *); - -static volatile int pci_master_aborts = 0; - -#ifdef DEBUG -#define DBG(x...) printk(x) -#else -#define DBG(x...) -#endif - -int clear_master_aborts(void); - -static u32 -*ixp23xx_pci_config_addr(unsigned int bus_nr, unsigned int devfn, int where) -{ - u32 *paddress; - - /* - * Must be dword aligned - */ - where &= ~3; - - /* - * For top bus, generate type 0, else type 1 - */ - if (!bus_nr) { - if (PCI_SLOT(devfn) >= 8) - return 0; - - paddress = (u32 *) (IXP23XX_PCI_CFG0_VIRT - | (1 << (PCI_SLOT(devfn) + 16)) - | (PCI_FUNC(devfn) << 8) | where); - } else { - paddress = (u32 *) (IXP23XX_PCI_CFG1_VIRT - | (bus_nr << 16) - | (PCI_SLOT(devfn) << 11) - | (PCI_FUNC(devfn) << 8) | where); - } - - return paddress; -} - -/* - * Mask table, bits to mask for quantity of size 1, 2 or 4 bytes. - * 0 and 3 are not valid indexes... - */ -static u32 bytemask[] = { - /*0*/ 0, - /*1*/ 0xff, - /*2*/ 0xffff, - /*3*/ 0, - /*4*/ 0xffffffff, -}; - -static int ixp23xx_pci_read_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *value) -{ - u32 n; - u32 *addr; - - n = where % 4; - - DBG("In config_read(%d) %d from dev %d:%d:%d\n", size, where, - bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); - - addr = ixp23xx_pci_config_addr(bus->number, devfn, where); - if (!addr) - return PCIBIOS_DEVICE_NOT_FOUND; - - pci_master_aborts = 0; - *value = (*addr >> (8*n)) & bytemask[size]; - if (pci_master_aborts) { - pci_master_aborts = 0; - *value = 0xffffffff; - return PCIBIOS_DEVICE_NOT_FOUND; - } - - return PCIBIOS_SUCCESSFUL; -} - -/* - * We don't do error checking on the address for writes. - * It's assumed that the user checked for the device existing first - * by doing a read first. - */ -static int ixp23xx_pci_write_config(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 value) -{ - u32 mask; - u32 *addr; - u32 temp; - - mask = ~(bytemask[size] << ((where % 0x4) * 8)); - addr = ixp23xx_pci_config_addr(bus->number, devfn, where); - if (!addr) - return PCIBIOS_DEVICE_NOT_FOUND; - temp = (u32) (value) << ((where % 0x4) * 8); - *addr = (*addr & mask) | temp; - - clear_master_aborts(); - - return PCIBIOS_SUCCESSFUL; -} - -struct pci_ops ixp23xx_pci_ops = { - .read = ixp23xx_pci_read_config, - .write = ixp23xx_pci_write_config, -}; - -struct pci_bus *ixp23xx_pci_scan_bus(int nr, struct pci_sys_data *sysdata) -{ - return pci_scan_root_bus(NULL, sysdata->busnr, &ixp23xx_pci_ops, - sysdata, &sysdata->resources); -} - -int ixp23xx_pci_abort_handler(unsigned long addr, unsigned int fsr, struct pt_regs *regs) -{ - volatile unsigned long temp; - unsigned long flags; - - pci_master_aborts = 1; - - local_irq_save(flags); - temp = *IXP23XX_PCI_CONTROL; - - /* - * master abort and cmd tgt err - */ - if (temp & ((1 << 8) | (1 << 5))) - *IXP23XX_PCI_CONTROL = temp; - - temp = *IXP23XX_PCI_CMDSTAT; - - if (temp & (1 << 29)) - *IXP23XX_PCI_CMDSTAT = temp; - local_irq_restore(flags); - - /* - * If it was an imprecise abort, then we need to correct the - * return address to be _after_ the instruction. - */ - if (fsr & (1 << 10)) - regs->ARM_pc += 4; - - return 0; -} - -int clear_master_aborts(void) -{ - volatile u32 temp; - - temp = *IXP23XX_PCI_CONTROL; - - /* - * master abort and cmd tgt err - */ - if (temp & ((1 << 8) | (1 << 5))) - *IXP23XX_PCI_CONTROL = temp; - - temp = *IXP23XX_PCI_CMDSTAT; - - if (temp & (1 << 29)) - *IXP23XX_PCI_CMDSTAT = temp; - - return 0; -} - -static void __init ixp23xx_pci_common_init(void) -{ -#ifdef __ARMEB__ - *IXP23XX_PCI_CONTROL |= 0x20000; /* set I/O swapping */ -#endif - /* - * ADDR_31 needs to be clear for PCI memory access to CPP memory - */ - *IXP23XX_CPP2XSI_CURR_XFER_REG3 &= ~IXP23XX_CPP2XSI_ADDR_31; - *IXP23XX_CPP2XSI_CURR_XFER_REG3 |= IXP23XX_CPP2XSI_PSH_OFF; - - /* - * Select correct memory for PCI inbound transactions - */ - if (ixp23xx_cpp_boot()) { - *IXP23XX_PCI_CPP_ADDR_BITS &= ~(1 << 1); - } else { - *IXP23XX_PCI_CPP_ADDR_BITS |= (1 << 1); - - /* - * Enable coherency on A2 silicon. - */ - if (arch_is_coherent()) - *IXP23XX_CPP2XSI_CURR_XFER_REG3 &= ~IXP23XX_CPP2XSI_COH_OFF; - } -} - -void __init ixp23xx_pci_preinit(void) -{ - pcibios_min_io = 0; - pcibios_min_mem = 0xe0000000; - - pci_set_flags(0); - - ixp23xx_pci_common_init(); - - hook_fault_code(16+6, ixp23xx_pci_abort_handler, SIGBUS, 0, - "PCI config cycle to non-existent device"); - - *IXP23XX_PCI_ADDR_EXT = 0x0000e000; -} - -/* - * Prevent PCI layer from seeing the inbound host-bridge resources - */ -static void __devinit pci_fixup_ixp23xx(struct pci_dev *dev) -{ - int i; - - dev->class &= 0xff; - dev->class |= PCI_CLASS_BRIDGE_HOST << 8; - for (i = 0; i < PCI_NUM_RESOURCES; i++) { - dev->resource[i].start = 0; - dev->resource[i].end = 0; - dev->resource[i].flags = 0; - } -} -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x9002, pci_fixup_ixp23xx); - -/* - * IXP2300 systems often have large resource requirements, so we just - * use our own resource space. - */ -static struct resource ixp23xx_pci_mem_space = { - .start = IXP23XX_PCI_MEM_START, - .end = IXP23XX_PCI_MEM_START + IXP23XX_PCI_MEM_SIZE - 1, - .flags = IORESOURCE_MEM, - .name = "PCI Mem Space" -}; - -static struct resource ixp23xx_pci_io_space = { - .start = 0x00000100, - .end = 0x01ffffff, - .flags = IORESOURCE_IO, - .name = "PCI I/O Space" -}; - -int ixp23xx_pci_setup(int nr, struct pci_sys_data *sys) -{ - if (nr >= 1) - return 0; - - pci_add_resource_offset(&sys->resources, - &ixp23xx_pci_io_space, sys->io_offset); - pci_add_resource_offset(&sys->resources, - &ixp23xx_pci_mem_space, sys->mem_offset); - - return 1; -} - -void __init ixp23xx_pci_slave_init(void) -{ - ixp23xx_pci_common_init(); -} diff --git a/arch/arm/mach-ixp23xx/roadrunner.c b/arch/arm/mach-ixp23xx/roadrunner.c deleted file mode 100644 index eaaa3fa9fd05..000000000000 --- a/arch/arm/mach-ixp23xx/roadrunner.c +++ /dev/null @@ -1,180 +0,0 @@ -/* - * arch/arm/mach-ixp23xx/roadrunner.c - * - * RoadRunner board-specific routines - * - * Author: Deepak Saxena - * - * Copyright 2005 (c) MontaVista Software, Inc. - * - * Based on 2.4 code Copyright 2005 (c) ADI Engineering Corporation - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -/* - * Interrupt mapping - */ -#define INTA IRQ_ROADRUNNER_PCI_INTA -#define INTB IRQ_ROADRUNNER_PCI_INTB -#define INTC IRQ_ROADRUNNER_PCI_INTC -#define INTD IRQ_ROADRUNNER_PCI_INTD - -#define INTC_PIN IXP23XX_GPIO_PIN_11 -#define INTD_PIN IXP23XX_GPIO_PIN_12 - -static int __init roadrunner_map_irq(const struct pci_dev *dev, u8 idsel, - u8 pin) -{ - static int pci_card_slot_irq[] = {INTB, INTC, INTD, INTA}; - static int pmc_card_slot_irq[] = {INTA, INTB, INTC, INTD}; - static int usb_irq[] = {INTB, INTC, INTD, -1}; - static int mini_pci_1_irq[] = {INTB, INTC, -1, -1}; - static int mini_pci_2_irq[] = {INTC, INTD, -1, -1}; - - switch(dev->bus->number) { - case 0: - switch(dev->devfn) { - case 0x0: // PCI-PCI bridge - break; - case 0x8: // PCI Card Slot - return pci_card_slot_irq[pin - 1]; - case 0x10: // PMC Slot - return pmc_card_slot_irq[pin - 1]; - case 0x18: // PMC Slot Secondary Agent - break; - case 0x20: // IXP Processor - break; - default: - return NO_IRQ; - } - break; - - case 1: - switch(dev->devfn) { - case 0x0: // IDE Controller - return (pin == 1) ? INTC : -1; - case 0x8: // USB fun 0 - case 0x9: // USB fun 1 - case 0xa: // USB fun 2 - return usb_irq[pin - 1]; - case 0x10: // Mini PCI 1 - return mini_pci_1_irq[pin-1]; - case 0x18: // Mini PCI 2 - return mini_pci_2_irq[pin-1]; - case 0x20: // MEM slot - return (pin == 1) ? INTA : -1; - default: - return NO_IRQ; - } - break; - - default: - return NO_IRQ; - } - - return NO_IRQ; -} - -static void __init roadrunner_pci_preinit(void) -{ - irq_set_irq_type(IRQ_ROADRUNNER_PCI_INTC, IRQ_TYPE_LEVEL_LOW); - irq_set_irq_type(IRQ_ROADRUNNER_PCI_INTD, IRQ_TYPE_LEVEL_LOW); - - ixp23xx_pci_preinit(); -} - -static struct hw_pci roadrunner_pci __initdata = { - .nr_controllers = 1, - .preinit = roadrunner_pci_preinit, - .setup = ixp23xx_pci_setup, - .scan = ixp23xx_pci_scan_bus, - .map_irq = roadrunner_map_irq, -}; - -static int __init roadrunner_pci_init(void) -{ - if (machine_is_roadrunner()) - pci_common_init(&roadrunner_pci); - - return 0; -}; - -subsys_initcall(roadrunner_pci_init); - -static struct physmap_flash_data roadrunner_flash_data = { - .width = 2, -}; - -static struct resource roadrunner_flash_resource = { - .start = 0x90000000, - .end = 0x93ffffff, - .flags = IORESOURCE_MEM, -}; - -static struct platform_device roadrunner_flash = { - .name = "physmap-flash", - .id = 0, - .dev = { - .platform_data = &roadrunner_flash_data, - }, - .num_resources = 1, - .resource = &roadrunner_flash_resource, -}; - -static void __init roadrunner_init(void) -{ - platform_device_register(&roadrunner_flash); - - /* - * Mark flash as writeable - */ - IXP23XX_EXP_CS0[0] |= IXP23XX_FLASH_WRITABLE; - IXP23XX_EXP_CS0[1] |= IXP23XX_FLASH_WRITABLE; - IXP23XX_EXP_CS0[2] |= IXP23XX_FLASH_WRITABLE; - IXP23XX_EXP_CS0[3] |= IXP23XX_FLASH_WRITABLE; - - ixp23xx_sys_init(); -} - -MACHINE_START(ROADRUNNER, "ADI Engineering RoadRunner Development Platform") - /* Maintainer: Deepak Saxena */ - .map_io = ixp23xx_map_io, - .init_irq = ixp23xx_init_irq, - .timer = &ixp23xx_timer, - .atag_offset = 0x100, - .init_machine = roadrunner_init, - .restart = ixp23xx_restart, -MACHINE_END -- cgit v1.2.3 From 896637ac1be95a239b68dbe61c12a8a9bc00a9a3 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 6 Apr 2012 10:30:52 -0600 Subject: ASoC: tegra: complete Tegra->Tegra20 renaming Rename Tegra20-specific Kconfig variables, module filenames, all internal symbol names, clocks, and platform devices, to reflect the fact the DAS and I2S drivers are for a specific HW version. Signed-off-by: Stephen Warren Signed-off-by: Mark Brown --- arch/arm/mach-tegra/board-dt-tegra20.c | 6 +- arch/arm/mach-tegra/devices.c | 6 +- arch/arm/mach-tegra/tegra2_clocks.c | 4 +- sound/soc/tegra/Kconfig | 20 +- sound/soc/tegra/Makefile | 6 +- sound/soc/tegra/tegra20_das.c | 134 +++++------ sound/soc/tegra/tegra20_das.h | 124 +++++----- sound/soc/tegra/tegra20_i2s.c | 204 ++++++++--------- sound/soc/tegra/tegra20_i2s.h | 246 ++++++++++---------- sound/soc/tegra/tegra20_spdif.c | 140 ++++++------ sound/soc/tegra/tegra20_spdif.h | 398 ++++++++++++++++----------------- sound/soc/tegra/tegra_wm8903.c | 4 +- sound/soc/tegra/trimslice.c | 4 +- 13 files changed, 648 insertions(+), 648 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-tegra/board-dt-tegra20.c b/arch/arm/mach-tegra/board-dt-tegra20.c index 0952494f481a..86abcf0d2be9 100644 --- a/arch/arm/mach-tegra/board-dt-tegra20.c +++ b/arch/arm/mach-tegra/board-dt-tegra20.c @@ -64,9 +64,9 @@ struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = { OF_DEV_AUXDATA("nvidia,tegra20-i2c", TEGRA_I2C2_BASE, "tegra-i2c.1", NULL), OF_DEV_AUXDATA("nvidia,tegra20-i2c", TEGRA_I2C3_BASE, "tegra-i2c.2", NULL), OF_DEV_AUXDATA("nvidia,tegra20-i2c-dvc", TEGRA_DVC_BASE, "tegra-i2c.3", NULL), - OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S1_BASE, "tegra-i2s.0", NULL), - OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S2_BASE, "tegra-i2s.1", NULL), - OF_DEV_AUXDATA("nvidia,tegra20-das", TEGRA_APB_MISC_DAS_BASE, "tegra-das", NULL), + OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S1_BASE, "tegra20-i2s.0", NULL), + OF_DEV_AUXDATA("nvidia,tegra20-i2s", TEGRA_I2S2_BASE, "tegra20-i2s.1", NULL), + OF_DEV_AUXDATA("nvidia,tegra20-das", TEGRA_APB_MISC_DAS_BASE, "tegra20-das", NULL), OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB_BASE, "tegra-ehci.0", &tegra_ehci1_pdata), OF_DEV_AUXDATA("nvidia,tegra20-ehci", TEGRA_USB2_BASE, "tegra-ehci.1", diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c index c3b990097f28..d6c9b2624d85 100644 --- a/arch/arm/mach-tegra/devices.c +++ b/arch/arm/mach-tegra/devices.c @@ -671,14 +671,14 @@ static struct resource i2s_resource2[] = { }; struct platform_device tegra_i2s_device1 = { - .name = "tegra-i2s", + .name = "tegra20-i2s", .id = 0, .resource = i2s_resource1, .num_resources = ARRAY_SIZE(i2s_resource1), }; struct platform_device tegra_i2s_device2 = { - .name = "tegra-i2s", + .name = "tegra20-i2s", .id = 1, .resource = i2s_resource2, .num_resources = ARRAY_SIZE(i2s_resource2), @@ -693,7 +693,7 @@ static struct resource tegra_das_resources[] = { }; struct platform_device tegra_das_device = { - .name = "tegra-das", + .name = "tegra20-das", .id = -1, .num_resources = ARRAY_SIZE(tegra_das_resources), .resource = tegra_das_resources, diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c index 592a4eeb5328..cf4999bcd4d8 100644 --- a/arch/arm/mach-tegra/tegra2_clocks.c +++ b/arch/arm/mach-tegra/tegra2_clocks.c @@ -2142,8 +2142,8 @@ static struct clk tegra_list_clks[] = { PERIPH_CLK("apbdma", "tegra-dma", NULL, 34, 0, 108000000, mux_pclk, 0), PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, 32768, mux_clk_32k, PERIPH_NO_RESET), PERIPH_CLK("timer", "timer", NULL, 5, 0, 26000000, mux_clk_m, 0), - PERIPH_CLK("i2s1", "tegra-i2s.0", NULL, 11, 0x100, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), - PERIPH_CLK("i2s2", "tegra-i2s.1", NULL, 18, 0x104, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), + PERIPH_CLK("i2s1", "tegra20-i2s.0", NULL, 11, 0x100, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), + PERIPH_CLK("i2s2", "tegra20-i2s.1", NULL, 18, 0x104, 26000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), PERIPH_CLK("spdif_out", "spdif_out", NULL, 10, 0x108, 100000000, mux_pllaout0_audio2x_pllp_clkm, MUX | DIV_U71), PERIPH_CLK("spdif_in", "spdif_in", NULL, 10, 0x10c, 100000000, mux_pllp_pllc_pllm, MUX | DIV_U71), PERIPH_CLK("pwm", "pwm", NULL, 17, 0x110, 432000000, mux_pllp_pllc_audio_clkm_clk32, MUX | DIV_U71), diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig index d51945e78017..556cac29ea15 100644 --- a/sound/soc/tegra/Kconfig +++ b/sound/soc/tegra/Kconfig @@ -4,29 +4,29 @@ config SND_SOC_TEGRA help Say Y or M here if you want support for SoC audio on Tegra. -config SND_SOC_TEGRA_DAS +config SND_SOC_TEGRA20_DAS tristate depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC help - Say Y or M if you want to add support for the Tegra DAS module. + Say Y or M if you want to add support for the Tegra20 DAS module. You will also need to select the individual machine drivers to support below. -config SND_SOC_TEGRA_I2S +config SND_SOC_TEGRA20_I2S tristate depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC - select SND_SOC_TEGRA_DAS + select SND_SOC_TEGRA20_DAS help Say Y or M if you want to add support for codecs attached to the - Tegra I2S interface. You will also need to select the individual + Tegra20 I2S interface. You will also need to select the individual machine drivers to support below. -config SND_SOC_TEGRA_SPDIF +config SND_SOC_TEGRA20_SPDIF tristate depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC default m help - Say Y or M if you want to add support for the SPDIF interface. + Say Y or M if you want to add support for the Tegra20 SPDIF interface. You will also need to select the individual machine drivers to support below. @@ -41,7 +41,7 @@ config SND_SOC_TEGRA_WM8903 tristate "SoC Audio support for Tegra boards using a WM8903 codec" depends on SND_SOC_TEGRA && I2C depends on MACH_HAS_SND_SOC_TEGRA_WM8903 - select SND_SOC_TEGRA_I2S if ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_WM8903 help Say Y or M here if you want to add support for SoC audio on Tegra @@ -51,7 +51,7 @@ config SND_SOC_TEGRA_WM8903 config SND_SOC_TEGRA_TRIMSLICE tristate "SoC Audio support for TrimSlice board" depends on SND_SOC_TEGRA && MACH_TRIMSLICE && I2C - select SND_SOC_TEGRA_I2S if ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_TLV320AIC23 help Say Y or M here if you want to add support for SoC audio on the @@ -60,7 +60,7 @@ config SND_SOC_TEGRA_TRIMSLICE config SND_SOC_TEGRA_ALC5632 tristate "SoC Audio support for Tegra boards using an ALC5632 codec" depends on SND_SOC_TEGRA && I2C - select SND_SOC_TEGRA_I2S if ARCH_TEGRA_2x_SOC + select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC select SND_SOC_ALC5632 help Say Y or M here if you want to add support for SoC audio on the diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile index 4a33884115f8..4726b909664d 100644 --- a/sound/soc/tegra/Makefile +++ b/sound/soc/tegra/Makefile @@ -7,9 +7,9 @@ snd-soc-tegra20-spdif-objs := tegra20_spdif.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-pcm.o obj-$(CONFIG_SND_SOC_TEGRA) += snd-soc-tegra-utils.o -obj-$(CONFIG_SND_SOC_TEGRA_DAS) += snd-soc-tegra20-das.o -obj-$(CONFIG_SND_SOC_TEGRA_I2S) += snd-soc-tegra20-i2s.o -obj-$(CONFIG_SND_SOC_TEGRA_SPDIF) += snd-soc-tegra20-spdif.o +obj-$(CONFIG_SND_SOC_TEGRA20_DAS) += snd-soc-tegra20-das.o +obj-$(CONFIG_SND_SOC_TEGRA20_I2S) += snd-soc-tegra20-i2s.o +obj-$(CONFIG_SND_SOC_TEGRA20_SPDIF) += snd-soc-tegra20-spdif.o # Tegra machine Support snd-soc-tegra-wm8903-objs := tegra_wm8903.o diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c index b5e369820cb3..486d7b245cf4 100644 --- a/sound/soc/tegra/tegra20_das.c +++ b/sound/soc/tegra/tegra20_das.c @@ -31,21 +31,21 @@ #include #include "tegra20_das.h" -#define DRV_NAME "tegra-das" +#define DRV_NAME "tegra20-das" -static struct tegra_das *das; +static struct tegra20_das *das; -static inline void tegra_das_write(u32 reg, u32 val) +static inline void tegra20_das_write(u32 reg, u32 val) { __raw_writel(val, das->regs + reg); } -static inline u32 tegra_das_read(u32 reg) +static inline u32 tegra20_das_read(u32 reg) { return __raw_readl(das->regs + reg); } -int tegra_das_connect_dap_to_dac(int dap, int dac) +int tegra20_das_connect_dap_to_dac(int dap, int dac) { u32 addr; u32 reg; @@ -53,18 +53,18 @@ int tegra_das_connect_dap_to_dac(int dap, int dac) if (!das) return -ENODEV; - addr = TEGRA_DAS_DAP_CTRL_SEL + - (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE); - reg = dac << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P; + addr = TEGRA20_DAS_DAP_CTRL_SEL + + (dap * TEGRA20_DAS_DAP_CTRL_SEL_STRIDE); + reg = dac << TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P; - tegra_das_write(addr, reg); + tegra20_das_write(addr, reg); return 0; } -EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dac); +EXPORT_SYMBOL_GPL(tegra20_das_connect_dap_to_dac); -int tegra_das_connect_dap_to_dap(int dap, int otherdap, int master, - int sdata1rx, int sdata2rx) +int tegra20_das_connect_dap_to_dap(int dap, int otherdap, int master, + int sdata1rx, int sdata2rx) { u32 addr; u32 reg; @@ -72,20 +72,20 @@ int tegra_das_connect_dap_to_dap(int dap, int otherdap, int master, if (!das) return -ENODEV; - addr = TEGRA_DAS_DAP_CTRL_SEL + - (dap * TEGRA_DAS_DAP_CTRL_SEL_STRIDE); - reg = otherdap << TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P | - !!sdata2rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P | - !!sdata1rx << TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P | - !!master << TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P; + addr = TEGRA20_DAS_DAP_CTRL_SEL + + (dap * TEGRA20_DAS_DAP_CTRL_SEL_STRIDE); + reg = otherdap << TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P | + !!sdata2rx << TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P | + !!sdata1rx << TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P | + !!master << TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P; - tegra_das_write(addr, reg); + tegra20_das_write(addr, reg); return 0; } -EXPORT_SYMBOL_GPL(tegra_das_connect_dap_to_dap); +EXPORT_SYMBOL_GPL(tegra20_das_connect_dap_to_dap); -int tegra_das_connect_dac_to_dap(int dac, int dap) +int tegra20_das_connect_dac_to_dap(int dac, int dap) { u32 addr; u32 reg; @@ -93,78 +93,78 @@ int tegra_das_connect_dac_to_dap(int dac, int dap) if (!das) return -ENODEV; - addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL + - (dac * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE); - reg = dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P | - dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P | - dap << TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P; + addr = TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL + + (dac * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE); + reg = dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P | + dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P | + dap << TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P; - tegra_das_write(addr, reg); + tegra20_das_write(addr, reg); return 0; } -EXPORT_SYMBOL_GPL(tegra_das_connect_dac_to_dap); +EXPORT_SYMBOL_GPL(tegra20_das_connect_dac_to_dap); #ifdef CONFIG_DEBUG_FS -static int tegra_das_show(struct seq_file *s, void *unused) +static int tegra20_das_show(struct seq_file *s, void *unused) { int i; u32 addr; u32 reg; - for (i = 0; i < TEGRA_DAS_DAP_CTRL_SEL_COUNT; i++) { - addr = TEGRA_DAS_DAP_CTRL_SEL + - (i * TEGRA_DAS_DAP_CTRL_SEL_STRIDE); - reg = tegra_das_read(addr); - seq_printf(s, "TEGRA_DAS_DAP_CTRL_SEL[%d] = %08x\n", i, reg); + for (i = 0; i < TEGRA20_DAS_DAP_CTRL_SEL_COUNT; i++) { + addr = TEGRA20_DAS_DAP_CTRL_SEL + + (i * TEGRA20_DAS_DAP_CTRL_SEL_STRIDE); + reg = tegra20_das_read(addr); + seq_printf(s, "TEGRA20_DAS_DAP_CTRL_SEL[%d] = %08x\n", i, reg); } - for (i = 0; i < TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT; i++) { - addr = TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL + - (i * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE); - reg = tegra_das_read(addr); - seq_printf(s, "TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL[%d] = %08x\n", - i, reg); + for (i = 0; i < TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT; i++) { + addr = TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL + + (i * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE); + reg = tegra20_das_read(addr); + seq_printf(s, "TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL[%d] = %08x\n", + i, reg); } return 0; } -static int tegra_das_debug_open(struct inode *inode, struct file *file) +static int tegra20_das_debug_open(struct inode *inode, struct file *file) { - return single_open(file, tegra_das_show, inode->i_private); + return single_open(file, tegra20_das_show, inode->i_private); } -static const struct file_operations tegra_das_debug_fops = { - .open = tegra_das_debug_open, +static const struct file_operations tegra20_das_debug_fops = { + .open = tegra20_das_debug_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; -static void tegra_das_debug_add(struct tegra_das *das) +static void tegra20_das_debug_add(struct tegra20_das *das) { das->debug = debugfs_create_file(DRV_NAME, S_IRUGO, snd_soc_debugfs_root, das, - &tegra_das_debug_fops); + &tegra20_das_debug_fops); } -static void tegra_das_debug_remove(struct tegra_das *das) +static void tegra20_das_debug_remove(struct tegra20_das *das) { if (das->debug) debugfs_remove(das->debug); } #else -static inline void tegra_das_debug_add(struct tegra_das *das) +static inline void tegra20_das_debug_add(struct tegra20_das *das) { } -static inline void tegra_das_debug_remove(struct tegra_das *das) +static inline void tegra20_das_debug_remove(struct tegra20_das *das) { } #endif -static int __devinit tegra_das_probe(struct platform_device *pdev) +static int __devinit tegra20_das_probe(struct platform_device *pdev) { struct resource *res, *region; int ret = 0; @@ -172,9 +172,9 @@ static int __devinit tegra_das_probe(struct platform_device *pdev) if (das) return -ENODEV; - das = devm_kzalloc(&pdev->dev, sizeof(struct tegra_das), GFP_KERNEL); + das = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_das), GFP_KERNEL); if (!das) { - dev_err(&pdev->dev, "Can't allocate tegra_das\n"); + dev_err(&pdev->dev, "Can't allocate tegra20_das\n"); ret = -ENOMEM; goto err; } @@ -202,20 +202,20 @@ static int __devinit tegra_das_probe(struct platform_device *pdev) goto err; } - ret = tegra_das_connect_dap_to_dac(TEGRA_DAS_DAP_ID_1, - TEGRA_DAS_DAP_SEL_DAC1); + ret = tegra20_das_connect_dap_to_dac(TEGRA20_DAS_DAP_ID_1, + TEGRA20_DAS_DAP_SEL_DAC1); if (ret) { dev_err(&pdev->dev, "Can't set up DAS DAP connection\n"); goto err; } - ret = tegra_das_connect_dac_to_dap(TEGRA_DAS_DAC_ID_1, - TEGRA_DAS_DAC_SEL_DAP1); + ret = tegra20_das_connect_dac_to_dap(TEGRA20_DAS_DAC_ID_1, + TEGRA20_DAS_DAC_SEL_DAP1); if (ret) { dev_err(&pdev->dev, "Can't set up DAS DAC connection\n"); goto err; } - tegra_das_debug_add(das); + tegra20_das_debug_add(das); platform_set_drvdata(pdev, das); @@ -226,36 +226,36 @@ err: return ret; } -static int __devexit tegra_das_remove(struct platform_device *pdev) +static int __devexit tegra20_das_remove(struct platform_device *pdev) { if (!das) return -ENODEV; - tegra_das_debug_remove(das); + tegra20_das_debug_remove(das); das = NULL; return 0; } -static const struct of_device_id tegra_das_of_match[] __devinitconst = { +static const struct of_device_id tegra20_das_of_match[] __devinitconst = { { .compatible = "nvidia,tegra20-das", }, {}, }; -static struct platform_driver tegra_das_driver = { - .probe = tegra_das_probe, - .remove = __devexit_p(tegra_das_remove), +static struct platform_driver tegra20_das_driver = { + .probe = tegra20_das_probe, + .remove = __devexit_p(tegra20_das_remove), .driver = { .name = DRV_NAME, .owner = THIS_MODULE, - .of_match_table = tegra_das_of_match, + .of_match_table = tegra20_das_of_match, }, }; -module_platform_driver(tegra_das_driver); +module_platform_driver(tegra20_das_driver); MODULE_AUTHOR("Stephen Warren "); -MODULE_DESCRIPTION("Tegra DAS driver"); +MODULE_DESCRIPTION("Tegra20 DAS driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRV_NAME); -MODULE_DEVICE_TABLE(of, tegra_das_of_match); +MODULE_DEVICE_TABLE(of, tegra20_das_of_match); diff --git a/sound/soc/tegra/tegra20_das.h b/sound/soc/tegra/tegra20_das.h index 896bf031a801..ade4fe080c45 100644 --- a/sound/soc/tegra/tegra20_das.h +++ b/sound/soc/tegra/tegra20_das.h @@ -2,7 +2,7 @@ * tegra20_das.h - Definitions for Tegra20 DAS driver * * Author: Stephen Warren - * Copyright (C) 2010 - NVIDIA, Inc. + * Copyright (C) 2010,2012 - NVIDIA, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -20,70 +20,70 @@ * */ -#ifndef __TEGRA_DAS_H__ -#define __TEGRA_DAS_H__ +#ifndef __TEGRA20_DAS_H__ +#define __TEGRA20_DAS_H__ -/* Register TEGRA_DAS_DAP_CTRL_SEL */ -#define TEGRA_DAS_DAP_CTRL_SEL 0x00 -#define TEGRA_DAS_DAP_CTRL_SEL_COUNT 5 -#define TEGRA_DAS_DAP_CTRL_SEL_STRIDE 4 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P 31 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S 1 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P 30 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S 1 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P 29 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S 1 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P 0 -#define TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S 5 +/* Register TEGRA20_DAS_DAP_CTRL_SEL */ +#define TEGRA20_DAS_DAP_CTRL_SEL 0x00 +#define TEGRA20_DAS_DAP_CTRL_SEL_COUNT 5 +#define TEGRA20_DAS_DAP_CTRL_SEL_STRIDE 4 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_P 31 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_MS_SEL_S 1 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_P 30 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA1_TX_RX_S 1 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_P 29 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_SDATA2_TX_RX_S 1 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_P 0 +#define TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL_S 5 -/* Values for field TEGRA_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */ -#define TEGRA_DAS_DAP_SEL_DAC1 0 -#define TEGRA_DAS_DAP_SEL_DAC2 1 -#define TEGRA_DAS_DAP_SEL_DAC3 2 -#define TEGRA_DAS_DAP_SEL_DAP1 16 -#define TEGRA_DAS_DAP_SEL_DAP2 17 -#define TEGRA_DAS_DAP_SEL_DAP3 18 -#define TEGRA_DAS_DAP_SEL_DAP4 19 -#define TEGRA_DAS_DAP_SEL_DAP5 20 +/* Values for field TEGRA20_DAS_DAP_CTRL_SEL_DAP_CTRL_SEL */ +#define TEGRA20_DAS_DAP_SEL_DAC1 0 +#define TEGRA20_DAS_DAP_SEL_DAC2 1 +#define TEGRA20_DAS_DAP_SEL_DAC3 2 +#define TEGRA20_DAS_DAP_SEL_DAP1 16 +#define TEGRA20_DAS_DAP_SEL_DAP2 17 +#define TEGRA20_DAS_DAP_SEL_DAP3 18 +#define TEGRA20_DAS_DAP_SEL_DAP4 19 +#define TEGRA20_DAS_DAP_SEL_DAP5 20 -/* Register TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL */ -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL 0x40 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT 3 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE 4 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P 28 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S 4 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P 24 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S 4 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P 0 -#define TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S 4 +/* Register TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL */ +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL 0x40 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_COUNT 3 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_STRIDE 4 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_P 28 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL_S 4 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_P 24 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL_S 4 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_P 0 +#define TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL_S 4 /* * Values for: - * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL - * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL - * TEGRA_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL + * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA2_SEL + * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_SDATA1_SEL + * TEGRA20_DAS_DAC_INPUT_DATA_CLK_SEL_DAC_CLK_SEL */ -#define TEGRA_DAS_DAC_SEL_DAP1 0 -#define TEGRA_DAS_DAC_SEL_DAP2 1 -#define TEGRA_DAS_DAC_SEL_DAP3 2 -#define TEGRA_DAS_DAC_SEL_DAP4 3 -#define TEGRA_DAS_DAC_SEL_DAP5 4 +#define TEGRA20_DAS_DAC_SEL_DAP1 0 +#define TEGRA20_DAS_DAC_SEL_DAP2 1 +#define TEGRA20_DAS_DAC_SEL_DAP3 2 +#define TEGRA20_DAS_DAC_SEL_DAP4 3 +#define TEGRA20_DAS_DAC_SEL_DAP5 4 /* * Names/IDs of the DACs/DAPs. */ -#define TEGRA_DAS_DAP_ID_1 0 -#define TEGRA_DAS_DAP_ID_2 1 -#define TEGRA_DAS_DAP_ID_3 2 -#define TEGRA_DAS_DAP_ID_4 3 -#define TEGRA_DAS_DAP_ID_5 4 +#define TEGRA20_DAS_DAP_ID_1 0 +#define TEGRA20_DAS_DAP_ID_2 1 +#define TEGRA20_DAS_DAP_ID_3 2 +#define TEGRA20_DAS_DAP_ID_4 3 +#define TEGRA20_DAS_DAP_ID_5 4 -#define TEGRA_DAS_DAC_ID_1 0 -#define TEGRA_DAS_DAC_ID_2 1 -#define TEGRA_DAS_DAC_ID_3 2 +#define TEGRA20_DAS_DAC_ID_1 0 +#define TEGRA20_DAS_DAC_ID_2 1 +#define TEGRA20_DAS_DAC_ID_3 2 -struct tegra_das { +struct tegra20_das { struct device *dev; void __iomem *regs; struct dentry *debug; @@ -107,29 +107,29 @@ struct tegra_das { /* * Connect a DAP to to a DAC - * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_* - * dac_sel: DAC to connect to: TEGRA_DAS_DAP_SEL_DAC* + * dap_id: DAP to connect: TEGRA20_DAS_DAP_ID_* + * dac_sel: DAC to connect to: TEGRA20_DAS_DAP_SEL_DAC* */ -extern int tegra_das_connect_dap_to_dac(int dap_id, int dac_sel); +extern int tegra20_das_connect_dap_to_dac(int dap_id, int dac_sel); /* * Connect a DAP to to another DAP - * dap_id: DAP to connect: TEGRA_DAS_DAP_ID_* - * other_dap_sel: DAP to connect to: TEGRA_DAS_DAP_SEL_DAP* + * dap_id: DAP to connect: TEGRA20_DAS_DAP_ID_* + * other_dap_sel: DAP to connect to: TEGRA20_DAS_DAP_SEL_DAP* * master: Is this DAP the master (1) or slave (0) * sdata1rx: Is this DAP's SDATA1 pin RX (1) or TX (0) * sdata2rx: Is this DAP's SDATA2 pin RX (1) or TX (0) */ -extern int tegra_das_connect_dap_to_dap(int dap_id, int other_dap_sel, - int master, int sdata1rx, - int sdata2rx); +extern int tegra20_das_connect_dap_to_dap(int dap_id, int other_dap_sel, + int master, int sdata1rx, + int sdata2rx); /* * Connect a DAC's input to a DAP * (DAC outputs are selected by the DAP) - * dac_id: DAC ID to connect: TEGRA_DAS_DAC_ID_* - * dap_sel: DAP to receive input from: TEGRA_DAS_DAC_SEL_DAP* + * dac_id: DAC ID to connect: TEGRA20_DAS_DAC_ID_* + * dap_sel: DAP to receive input from: TEGRA20_DAS_DAC_SEL_DAP* */ -extern int tegra_das_connect_dac_to_dap(int dac_id, int dap_sel); +extern int tegra20_das_connect_dac_to_dap(int dac_id, int dap_sel); #endif diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c index e24759ae291b..878798c89e49 100644 --- a/sound/soc/tegra/tegra20_i2s.c +++ b/sound/soc/tegra/tegra20_i2s.c @@ -45,86 +45,86 @@ #include "tegra20_i2s.h" -#define DRV_NAME "tegra-i2s" +#define DRV_NAME "tegra20-i2s" -static inline void tegra_i2s_write(struct tegra_i2s *i2s, u32 reg, u32 val) +static inline void tegra20_i2s_write(struct tegra20_i2s *i2s, u32 reg, u32 val) { __raw_writel(val, i2s->regs + reg); } -static inline u32 tegra_i2s_read(struct tegra_i2s *i2s, u32 reg) +static inline u32 tegra20_i2s_read(struct tegra20_i2s *i2s, u32 reg) { return __raw_readl(i2s->regs + reg); } #ifdef CONFIG_DEBUG_FS -static int tegra_i2s_show(struct seq_file *s, void *unused) +static int tegra20_i2s_show(struct seq_file *s, void *unused) { #define REG(r) { r, #r } static const struct { int offset; const char *name; } regs[] = { - REG(TEGRA_I2S_CTRL), - REG(TEGRA_I2S_STATUS), - REG(TEGRA_I2S_TIMING), - REG(TEGRA_I2S_FIFO_SCR), - REG(TEGRA_I2S_PCM_CTRL), - REG(TEGRA_I2S_NW_CTRL), - REG(TEGRA_I2S_TDM_CTRL), - REG(TEGRA_I2S_TDM_TX_RX_CTRL), + REG(TEGRA20_I2S_CTRL), + REG(TEGRA20_I2S_STATUS), + REG(TEGRA20_I2S_TIMING), + REG(TEGRA20_I2S_FIFO_SCR), + REG(TEGRA20_I2S_PCM_CTRL), + REG(TEGRA20_I2S_NW_CTRL), + REG(TEGRA20_I2S_TDM_CTRL), + REG(TEGRA20_I2S_TDM_TX_RX_CTRL), }; #undef REG - struct tegra_i2s *i2s = s->private; + struct tegra20_i2s *i2s = s->private; int i; for (i = 0; i < ARRAY_SIZE(regs); i++) { - u32 val = tegra_i2s_read(i2s, regs[i].offset); + u32 val = tegra20_i2s_read(i2s, regs[i].offset); seq_printf(s, "%s = %08x\n", regs[i].name, val); } return 0; } -static int tegra_i2s_debug_open(struct inode *inode, struct file *file) +static int tegra20_i2s_debug_open(struct inode *inode, struct file *file) { - return single_open(file, tegra_i2s_show, inode->i_private); + return single_open(file, tegra20_i2s_show, inode->i_private); } -static const struct file_operations tegra_i2s_debug_fops = { - .open = tegra_i2s_debug_open, +static const struct file_operations tegra20_i2s_debug_fops = { + .open = tegra20_i2s_debug_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; -static void tegra_i2s_debug_add(struct tegra_i2s *i2s) +static void tegra20_i2s_debug_add(struct tegra20_i2s *i2s) { i2s->debug = debugfs_create_file(i2s->dai.name, S_IRUGO, snd_soc_debugfs_root, i2s, - &tegra_i2s_debug_fops); + &tegra20_i2s_debug_fops); } -static void tegra_i2s_debug_remove(struct tegra_i2s *i2s) +static void tegra20_i2s_debug_remove(struct tegra20_i2s *i2s) { if (i2s->debug) debugfs_remove(i2s->debug); } #else -static inline void tegra_i2s_debug_add(struct tegra_i2s *i2s, int id) +static inline void tegra20_i2s_debug_add(struct tegra20_i2s *i2s, int id) { } -static inline void tegra_i2s_debug_remove(struct tegra_i2s *i2s) +static inline void tegra20_i2s_debug_remove(struct tegra20_i2s *i2s) { } #endif -static int tegra_i2s_set_fmt(struct snd_soc_dai *dai, +static int tegra20_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); + struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: @@ -133,10 +133,10 @@ static int tegra_i2s_set_fmt(struct snd_soc_dai *dai, return -EINVAL; } - i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_MASTER_ENABLE; + i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_MASTER_ENABLE; switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_MASTER_ENABLE; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_MASTER_ENABLE; break; case SND_SOC_DAIFMT_CBM_CFM: break; @@ -144,28 +144,28 @@ static int tegra_i2s_set_fmt(struct snd_soc_dai *dai, return -EINVAL; } - i2s->reg_ctrl &= ~(TEGRA_I2S_CTRL_BIT_FORMAT_MASK | - TEGRA_I2S_CTRL_LRCK_MASK); + i2s->reg_ctrl &= ~(TEGRA20_I2S_CTRL_BIT_FORMAT_MASK | + TEGRA20_I2S_CTRL_LRCK_MASK); switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_DSP_A: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP; - i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW; break; case SND_SOC_DAIFMT_DSP_B: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_DSP; - i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_R_LOW; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_DSP; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_R_LOW; break; case SND_SOC_DAIFMT_I2S: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_I2S; - i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_I2S; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW; break; case SND_SOC_DAIFMT_RIGHT_J: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_RJM; - i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_RJM; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW; break; case SND_SOC_DAIFMT_LEFT_J: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_FORMAT_LJM; - i2s->reg_ctrl |= TEGRA_I2S_CTRL_LRCK_L_LOW; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_FORMAT_LJM; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_LRCK_L_LOW; break; default: return -EINVAL; @@ -174,27 +174,27 @@ static int tegra_i2s_set_fmt(struct snd_soc_dai *dai, return 0; } -static int tegra_i2s_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *params, - struct snd_soc_dai *dai) +static int tegra20_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { struct device *dev = substream->pcm->card->dev; - struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); + struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); u32 reg; int ret, sample_size, srate, i2sclock, bitcnt; - i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_BIT_SIZE_MASK; + i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_BIT_SIZE_MASK; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_16; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_SIZE_16; sample_size = 16; break; case SNDRV_PCM_FORMAT_S24_LE: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_24; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_SIZE_24; sample_size = 24; break; case SNDRV_PCM_FORMAT_S32_LE: - i2s->reg_ctrl |= TEGRA_I2S_CTRL_BIT_SIZE_32; + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_BIT_SIZE_32; sample_size = 32; break; default: @@ -213,54 +213,54 @@ static int tegra_i2s_hw_params(struct snd_pcm_substream *substream, } bitcnt = (i2sclock / (2 * srate)) - 1; - if (bitcnt < 0 || bitcnt > TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US) + if (bitcnt < 0 || bitcnt > TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US) return -EINVAL; - reg = bitcnt << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT; + reg = bitcnt << TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT; if (i2sclock % (2 * srate)) - reg |= TEGRA_I2S_TIMING_NON_SYM_ENABLE; + reg |= TEGRA20_I2S_TIMING_NON_SYM_ENABLE; clk_enable(i2s->clk_i2s); - tegra_i2s_write(i2s, TEGRA_I2S_TIMING, reg); + tegra20_i2s_write(i2s, TEGRA20_I2S_TIMING, reg); - tegra_i2s_write(i2s, TEGRA_I2S_FIFO_SCR, - TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS | - TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS); + tegra20_i2s_write(i2s, TEGRA20_I2S_FIFO_SCR, + TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS | + TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS); clk_disable(i2s->clk_i2s); return 0; } -static void tegra_i2s_start_playback(struct tegra_i2s *i2s) +static void tegra20_i2s_start_playback(struct tegra20_i2s *i2s) { - i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO1_ENABLE; - tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_FIFO1_ENABLE; + tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl); } -static void tegra_i2s_stop_playback(struct tegra_i2s *i2s) +static void tegra20_i2s_stop_playback(struct tegra20_i2s *i2s) { - i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO1_ENABLE; - tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); + i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_FIFO1_ENABLE; + tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl); } -static void tegra_i2s_start_capture(struct tegra_i2s *i2s) +static void tegra20_i2s_start_capture(struct tegra20_i2s *i2s) { - i2s->reg_ctrl |= TEGRA_I2S_CTRL_FIFO2_ENABLE; - tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); + i2s->reg_ctrl |= TEGRA20_I2S_CTRL_FIFO2_ENABLE; + tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl); } -static void tegra_i2s_stop_capture(struct tegra_i2s *i2s) +static void tegra20_i2s_stop_capture(struct tegra20_i2s *i2s) { - i2s->reg_ctrl &= ~TEGRA_I2S_CTRL_FIFO2_ENABLE; - tegra_i2s_write(i2s, TEGRA_I2S_CTRL, i2s->reg_ctrl); + i2s->reg_ctrl &= ~TEGRA20_I2S_CTRL_FIFO2_ENABLE; + tegra20_i2s_write(i2s, TEGRA20_I2S_CTRL, i2s->reg_ctrl); } -static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd, - struct snd_soc_dai *dai) +static int tegra20_i2s_trigger(struct snd_pcm_substream *substream, int cmd, + struct snd_soc_dai *dai) { - struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); + struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); switch (cmd) { case SNDRV_PCM_TRIGGER_START: @@ -268,17 +268,17 @@ static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_RESUME: clk_enable(i2s->clk_i2s); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - tegra_i2s_start_playback(i2s); + tegra20_i2s_start_playback(i2s); else - tegra_i2s_start_capture(i2s); + tegra20_i2s_start_capture(i2s); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - tegra_i2s_stop_playback(i2s); + tegra20_i2s_stop_playback(i2s); else - tegra_i2s_stop_capture(i2s); + tegra20_i2s_stop_capture(i2s); clk_disable(i2s->clk_i2s); break; default: @@ -288,9 +288,9 @@ static int tegra_i2s_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static int tegra_i2s_probe(struct snd_soc_dai *dai) +static int tegra20_i2s_probe(struct snd_soc_dai *dai) { - struct tegra_i2s *i2s = snd_soc_dai_get_drvdata(dai); + struct tegra20_i2s *i2s = snd_soc_dai_get_drvdata(dai); dai->capture_dma_data = &i2s->capture_dma_data; dai->playback_dma_data = &i2s->playback_dma_data; @@ -298,14 +298,14 @@ static int tegra_i2s_probe(struct snd_soc_dai *dai) return 0; } -static const struct snd_soc_dai_ops tegra_i2s_dai_ops = { - .set_fmt = tegra_i2s_set_fmt, - .hw_params = tegra_i2s_hw_params, - .trigger = tegra_i2s_trigger, +static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = { + .set_fmt = tegra20_i2s_set_fmt, + .hw_params = tegra20_i2s_hw_params, + .trigger = tegra20_i2s_trigger, }; -static const struct snd_soc_dai_driver tegra_i2s_dai_template = { - .probe = tegra_i2s_probe, +static const struct snd_soc_dai_driver tegra20_i2s_dai_template = { + .probe = tegra20_i2s_probe, .playback = { .channels_min = 2, .channels_max = 2, @@ -318,27 +318,27 @@ static const struct snd_soc_dai_driver tegra_i2s_dai_template = { .rates = SNDRV_PCM_RATE_8000_96000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, - .ops = &tegra_i2s_dai_ops, + .ops = &tegra20_i2s_dai_ops, .symmetric_rates = 1, }; -static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev) +static __devinit int tegra20_i2s_platform_probe(struct platform_device *pdev) { - struct tegra_i2s *i2s; + struct tegra20_i2s *i2s; struct resource *mem, *memregion, *dmareq; u32 of_dma[2]; u32 dma_ch; int ret; - i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra_i2s), GFP_KERNEL); + i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_i2s), GFP_KERNEL); if (!i2s) { - dev_err(&pdev->dev, "Can't allocate tegra_i2s\n"); + dev_err(&pdev->dev, "Can't allocate tegra20_i2s\n"); ret = -ENOMEM; goto err; } dev_set_drvdata(&pdev->dev, i2s); - i2s->dai = tegra_i2s_dai_template; + i2s->dai = tegra20_i2s_dai_template; i2s->dai.name = dev_name(&pdev->dev); i2s->clk_i2s = clk_get(&pdev->dev, NULL); @@ -384,17 +384,17 @@ static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev) goto err_clk_put; } - i2s->capture_dma_data.addr = mem->start + TEGRA_I2S_FIFO2; + i2s->capture_dma_data.addr = mem->start + TEGRA20_I2S_FIFO2; i2s->capture_dma_data.wrap = 4; i2s->capture_dma_data.width = 32; i2s->capture_dma_data.req_sel = dma_ch; - i2s->playback_dma_data.addr = mem->start + TEGRA_I2S_FIFO1; + i2s->playback_dma_data.addr = mem->start + TEGRA20_I2S_FIFO1; i2s->playback_dma_data.wrap = 4; i2s->playback_dma_data.width = 32; i2s->playback_dma_data.req_sel = dma_ch; - i2s->reg_ctrl = TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED; + i2s->reg_ctrl = TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED; ret = snd_soc_register_dai(&pdev->dev, &i2s->dai); if (ret) { @@ -409,7 +409,7 @@ static __devinit int tegra_i2s_platform_probe(struct platform_device *pdev) goto err_unregister_dai; } - tegra_i2s_debug_add(i2s); + tegra20_i2s_debug_add(i2s); return 0; @@ -421,38 +421,38 @@ err: return ret; } -static int __devexit tegra_i2s_platform_remove(struct platform_device *pdev) +static int __devexit tegra20_i2s_platform_remove(struct platform_device *pdev) { - struct tegra_i2s *i2s = dev_get_drvdata(&pdev->dev); + struct tegra20_i2s *i2s = dev_get_drvdata(&pdev->dev); tegra_pcm_platform_unregister(&pdev->dev); snd_soc_unregister_dai(&pdev->dev); - tegra_i2s_debug_remove(i2s); + tegra20_i2s_debug_remove(i2s); clk_put(i2s->clk_i2s); return 0; } -static const struct of_device_id tegra_i2s_of_match[] __devinitconst = { +static const struct of_device_id tegra20_i2s_of_match[] __devinitconst = { { .compatible = "nvidia,tegra20-i2s", }, {}, }; -static struct platform_driver tegra_i2s_driver = { +static struct platform_driver tegra20_i2s_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, - .of_match_table = tegra_i2s_of_match, + .of_match_table = tegra20_i2s_of_match, }, - .probe = tegra_i2s_platform_probe, - .remove = __devexit_p(tegra_i2s_platform_remove), + .probe = tegra20_i2s_platform_probe, + .remove = __devexit_p(tegra20_i2s_platform_remove), }; -module_platform_driver(tegra_i2s_driver); +module_platform_driver(tegra20_i2s_driver); MODULE_AUTHOR("Stephen Warren "); -MODULE_DESCRIPTION("Tegra I2S ASoC driver"); +MODULE_DESCRIPTION("Tegra20 I2S ASoC driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRV_NAME); -MODULE_DEVICE_TABLE(of, tegra_i2s_of_match); +MODULE_DEVICE_TABLE(of, tegra20_i2s_of_match); diff --git a/sound/soc/tegra/tegra20_i2s.h b/sound/soc/tegra/tegra20_i2s.h index 56f1e0f87c0b..86ab327dbd26 100644 --- a/sound/soc/tegra/tegra20_i2s.h +++ b/sound/soc/tegra/tegra20_i2s.h @@ -2,7 +2,7 @@ * tegra20_i2s.h - Definitions for Tegra20 I2S driver * * Author: Stephen Warren - * Copyright (C) 2010 - NVIDIA, Inc. + * Copyright (C) 2010,2012 - NVIDIA, Inc. * * Based on code copyright/by: * @@ -28,131 +28,131 @@ * */ -#ifndef __TEGRA_I2S_H__ -#define __TEGRA_I2S_H__ +#ifndef __TEGRA20_I2S_H__ +#define __TEGRA20_I2S_H__ #include "tegra_pcm.h" -/* Register offsets from TEGRA_I2S1_BASE and TEGRA_I2S2_BASE */ - -#define TEGRA_I2S_CTRL 0x00 -#define TEGRA_I2S_STATUS 0x04 -#define TEGRA_I2S_TIMING 0x08 -#define TEGRA_I2S_FIFO_SCR 0x0c -#define TEGRA_I2S_PCM_CTRL 0x10 -#define TEGRA_I2S_NW_CTRL 0x14 -#define TEGRA_I2S_TDM_CTRL 0x20 -#define TEGRA_I2S_TDM_TX_RX_CTRL 0x24 -#define TEGRA_I2S_FIFO1 0x40 -#define TEGRA_I2S_FIFO2 0x80 - -/* Fields in TEGRA_I2S_CTRL */ - -#define TEGRA_I2S_CTRL_FIFO2_TX_ENABLE (1 << 30) -#define TEGRA_I2S_CTRL_FIFO1_ENABLE (1 << 29) -#define TEGRA_I2S_CTRL_FIFO2_ENABLE (1 << 28) -#define TEGRA_I2S_CTRL_FIFO1_RX_ENABLE (1 << 27) -#define TEGRA_I2S_CTRL_FIFO_LPBK_ENABLE (1 << 26) -#define TEGRA_I2S_CTRL_MASTER_ENABLE (1 << 25) - -#define TEGRA_I2S_LRCK_LEFT_LOW 0 -#define TEGRA_I2S_LRCK_RIGHT_LOW 1 - -#define TEGRA_I2S_CTRL_LRCK_SHIFT 24 -#define TEGRA_I2S_CTRL_LRCK_MASK (1 << TEGRA_I2S_CTRL_LRCK_SHIFT) -#define TEGRA_I2S_CTRL_LRCK_L_LOW (TEGRA_I2S_LRCK_LEFT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT) -#define TEGRA_I2S_CTRL_LRCK_R_LOW (TEGRA_I2S_LRCK_RIGHT_LOW << TEGRA_I2S_CTRL_LRCK_SHIFT) - -#define TEGRA_I2S_BIT_FORMAT_I2S 0 -#define TEGRA_I2S_BIT_FORMAT_RJM 1 -#define TEGRA_I2S_BIT_FORMAT_LJM 2 -#define TEGRA_I2S_BIT_FORMAT_DSP 3 - -#define TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT 10 -#define TEGRA_I2S_CTRL_BIT_FORMAT_MASK (3 << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_BIT_FORMAT_I2S (TEGRA_I2S_BIT_FORMAT_I2S << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_BIT_FORMAT_RJM (TEGRA_I2S_BIT_FORMAT_RJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_BIT_FORMAT_LJM (TEGRA_I2S_BIT_FORMAT_LJM << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_BIT_FORMAT_DSP (TEGRA_I2S_BIT_FORMAT_DSP << TEGRA_I2S_CTRL_BIT_FORMAT_SHIFT) - -#define TEGRA_I2S_BIT_SIZE_16 0 -#define TEGRA_I2S_BIT_SIZE_20 1 -#define TEGRA_I2S_BIT_SIZE_24 2 -#define TEGRA_I2S_BIT_SIZE_32 3 - -#define TEGRA_I2S_CTRL_BIT_SIZE_SHIFT 8 -#define TEGRA_I2S_CTRL_BIT_SIZE_MASK (3 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) -#define TEGRA_I2S_CTRL_BIT_SIZE_16 (TEGRA_I2S_BIT_SIZE_16 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) -#define TEGRA_I2S_CTRL_BIT_SIZE_20 (TEGRA_I2S_BIT_SIZE_20 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) -#define TEGRA_I2S_CTRL_BIT_SIZE_24 (TEGRA_I2S_BIT_SIZE_24 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) -#define TEGRA_I2S_CTRL_BIT_SIZE_32 (TEGRA_I2S_BIT_SIZE_32 << TEGRA_I2S_CTRL_BIT_SIZE_SHIFT) - -#define TEGRA_I2S_FIFO_16_LSB 0 -#define TEGRA_I2S_FIFO_20_LSB 1 -#define TEGRA_I2S_FIFO_24_LSB 2 -#define TEGRA_I2S_FIFO_32 3 -#define TEGRA_I2S_FIFO_PACKED 7 - -#define TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT 4 -#define TEGRA_I2S_CTRL_FIFO_FORMAT_MASK (7 << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_FIFO_FORMAT_16_LSB (TEGRA_I2S_FIFO_16_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_FIFO_FORMAT_20_LSB (TEGRA_I2S_FIFO_20_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_FIFO_FORMAT_24_LSB (TEGRA_I2S_FIFO_24_LSB << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_FIFO_FORMAT_32 (TEGRA_I2S_FIFO_32 << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) -#define TEGRA_I2S_CTRL_FIFO_FORMAT_PACKED (TEGRA_I2S_FIFO_PACKED << TEGRA_I2S_CTRL_FIFO_FORMAT_SHIFT) - -#define TEGRA_I2S_CTRL_IE_FIFO1_ERR (1 << 3) -#define TEGRA_I2S_CTRL_IE_FIFO2_ERR (1 << 2) -#define TEGRA_I2S_CTRL_QE_FIFO1 (1 << 1) -#define TEGRA_I2S_CTRL_QE_FIFO2 (1 << 0) - -/* Fields in TEGRA_I2S_STATUS */ - -#define TEGRA_I2S_STATUS_FIFO1_RDY (1 << 31) -#define TEGRA_I2S_STATUS_FIFO2_RDY (1 << 30) -#define TEGRA_I2S_STATUS_FIFO1_BSY (1 << 29) -#define TEGRA_I2S_STATUS_FIFO2_BSY (1 << 28) -#define TEGRA_I2S_STATUS_FIFO1_ERR (1 << 3) -#define TEGRA_I2S_STATUS_FIFO2_ERR (1 << 2) -#define TEGRA_I2S_STATUS_QS_FIFO1 (1 << 1) -#define TEGRA_I2S_STATUS_QS_FIFO2 (1 << 0) - -/* Fields in TEGRA_I2S_TIMING */ - -#define TEGRA_I2S_TIMING_NON_SYM_ENABLE (1 << 12) -#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0 -#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7fff -#define TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT) - -/* Fields in TEGRA_I2S_FIFO_SCR */ - -#define TEGRA_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT 24 -#define TEGRA_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT 16 -#define TEGRA_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK 0x3f - -#define TEGRA_I2S_FIFO_SCR_FIFO2_CLR (1 << 12) -#define TEGRA_I2S_FIFO_SCR_FIFO1_CLR (1 << 8) - -#define TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT 0 -#define TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS 1 -#define TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS 2 -#define TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS 3 - -#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT 4 -#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK (3 << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) - -#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT 0 -#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK (3 << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT (TEGRA_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) -#define TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS (TEGRA_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) - -struct tegra_i2s { +/* Register offsets from TEGRA20_I2S1_BASE and TEGRA20_I2S2_BASE */ + +#define TEGRA20_I2S_CTRL 0x00 +#define TEGRA20_I2S_STATUS 0x04 +#define TEGRA20_I2S_TIMING 0x08 +#define TEGRA20_I2S_FIFO_SCR 0x0c +#define TEGRA20_I2S_PCM_CTRL 0x10 +#define TEGRA20_I2S_NW_CTRL 0x14 +#define TEGRA20_I2S_TDM_CTRL 0x20 +#define TEGRA20_I2S_TDM_TX_RX_CTRL 0x24 +#define TEGRA20_I2S_FIFO1 0x40 +#define TEGRA20_I2S_FIFO2 0x80 + +/* Fields in TEGRA20_I2S_CTRL */ + +#define TEGRA20_I2S_CTRL_FIFO2_TX_ENABLE (1 << 30) +#define TEGRA20_I2S_CTRL_FIFO1_ENABLE (1 << 29) +#define TEGRA20_I2S_CTRL_FIFO2_ENABLE (1 << 28) +#define TEGRA20_I2S_CTRL_FIFO1_RX_ENABLE (1 << 27) +#define TEGRA20_I2S_CTRL_FIFO_LPBK_ENABLE (1 << 26) +#define TEGRA20_I2S_CTRL_MASTER_ENABLE (1 << 25) + +#define TEGRA20_I2S_LRCK_LEFT_LOW 0 +#define TEGRA20_I2S_LRCK_RIGHT_LOW 1 + +#define TEGRA20_I2S_CTRL_LRCK_SHIFT 24 +#define TEGRA20_I2S_CTRL_LRCK_MASK (1 << TEGRA20_I2S_CTRL_LRCK_SHIFT) +#define TEGRA20_I2S_CTRL_LRCK_L_LOW (TEGRA20_I2S_LRCK_LEFT_LOW << TEGRA20_I2S_CTRL_LRCK_SHIFT) +#define TEGRA20_I2S_CTRL_LRCK_R_LOW (TEGRA20_I2S_LRCK_RIGHT_LOW << TEGRA20_I2S_CTRL_LRCK_SHIFT) + +#define TEGRA20_I2S_BIT_FORMAT_I2S 0 +#define TEGRA20_I2S_BIT_FORMAT_RJM 1 +#define TEGRA20_I2S_BIT_FORMAT_LJM 2 +#define TEGRA20_I2S_BIT_FORMAT_DSP 3 + +#define TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT 10 +#define TEGRA20_I2S_CTRL_BIT_FORMAT_MASK (3 << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_FORMAT_I2S (TEGRA20_I2S_BIT_FORMAT_I2S << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_FORMAT_RJM (TEGRA20_I2S_BIT_FORMAT_RJM << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_FORMAT_LJM (TEGRA20_I2S_BIT_FORMAT_LJM << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_FORMAT_DSP (TEGRA20_I2S_BIT_FORMAT_DSP << TEGRA20_I2S_CTRL_BIT_FORMAT_SHIFT) + +#define TEGRA20_I2S_BIT_SIZE_16 0 +#define TEGRA20_I2S_BIT_SIZE_20 1 +#define TEGRA20_I2S_BIT_SIZE_24 2 +#define TEGRA20_I2S_BIT_SIZE_32 3 + +#define TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT 8 +#define TEGRA20_I2S_CTRL_BIT_SIZE_MASK (3 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_SIZE_16 (TEGRA20_I2S_BIT_SIZE_16 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_SIZE_20 (TEGRA20_I2S_BIT_SIZE_20 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_SIZE_24 (TEGRA20_I2S_BIT_SIZE_24 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT) +#define TEGRA20_I2S_CTRL_BIT_SIZE_32 (TEGRA20_I2S_BIT_SIZE_32 << TEGRA20_I2S_CTRL_BIT_SIZE_SHIFT) + +#define TEGRA20_I2S_FIFO_16_LSB 0 +#define TEGRA20_I2S_FIFO_20_LSB 1 +#define TEGRA20_I2S_FIFO_24_LSB 2 +#define TEGRA20_I2S_FIFO_32 3 +#define TEGRA20_I2S_FIFO_PACKED 7 + +#define TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT 4 +#define TEGRA20_I2S_CTRL_FIFO_FORMAT_MASK (7 << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_FIFO_FORMAT_16_LSB (TEGRA20_I2S_FIFO_16_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_FIFO_FORMAT_20_LSB (TEGRA20_I2S_FIFO_20_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_FIFO_FORMAT_24_LSB (TEGRA20_I2S_FIFO_24_LSB << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_FIFO_FORMAT_32 (TEGRA20_I2S_FIFO_32 << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT) +#define TEGRA20_I2S_CTRL_FIFO_FORMAT_PACKED (TEGRA20_I2S_FIFO_PACKED << TEGRA20_I2S_CTRL_FIFO_FORMAT_SHIFT) + +#define TEGRA20_I2S_CTRL_IE_FIFO1_ERR (1 << 3) +#define TEGRA20_I2S_CTRL_IE_FIFO2_ERR (1 << 2) +#define TEGRA20_I2S_CTRL_QE_FIFO1 (1 << 1) +#define TEGRA20_I2S_CTRL_QE_FIFO2 (1 << 0) + +/* Fields in TEGRA20_I2S_STATUS */ + +#define TEGRA20_I2S_STATUS_FIFO1_RDY (1 << 31) +#define TEGRA20_I2S_STATUS_FIFO2_RDY (1 << 30) +#define TEGRA20_I2S_STATUS_FIFO1_BSY (1 << 29) +#define TEGRA20_I2S_STATUS_FIFO2_BSY (1 << 28) +#define TEGRA20_I2S_STATUS_FIFO1_ERR (1 << 3) +#define TEGRA20_I2S_STATUS_FIFO2_ERR (1 << 2) +#define TEGRA20_I2S_STATUS_QS_FIFO1 (1 << 1) +#define TEGRA20_I2S_STATUS_QS_FIFO2 (1 << 0) + +/* Fields in TEGRA20_I2S_TIMING */ + +#define TEGRA20_I2S_TIMING_NON_SYM_ENABLE (1 << 12) +#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT 0 +#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US 0x7fff +#define TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK (TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_MASK_US << TEGRA20_I2S_TIMING_CHANNEL_BIT_COUNT_SHIFT) + +/* Fields in TEGRA20_I2S_FIFO_SCR */ + +#define TEGRA20_I2S_FIFO_SCR_FIFO2_FULL_EMPTY_COUNT_SHIFT 24 +#define TEGRA20_I2S_FIFO_SCR_FIFO1_FULL_EMPTY_COUNT_SHIFT 16 +#define TEGRA20_I2S_FIFO_SCR_FIFO_FULL_EMPTY_COUNT_MASK 0x3f + +#define TEGRA20_I2S_FIFO_SCR_FIFO2_CLR (1 << 12) +#define TEGRA20_I2S_FIFO_SCR_FIFO1_CLR (1 << 8) + +#define TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT 0 +#define TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS 1 +#define TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS 2 +#define TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS 3 + +#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT 4 +#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_MASK (3 << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_ONE_SLOT (TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_FOUR_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_EIGHT_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_TWELVE_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO2_ATN_LVL_SHIFT) + +#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT 0 +#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_MASK (3 << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_ONE_SLOT (TEGRA20_I2S_FIFO_ATN_LVL_ONE_SLOT << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_FOUR_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_FOUR_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_EIGHT_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_EIGHT_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) +#define TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_TWELVE_SLOTS (TEGRA20_I2S_FIFO_ATN_LVL_TWELVE_SLOTS << TEGRA20_I2S_FIFO_SCR_FIFO1_ATN_LVL_SHIFT) + +struct tegra20_i2s { struct snd_soc_dai_driver dai; struct clk *clk_i2s; struct tegra_pcm_dma_params capture_dma_data; diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c index ed1fd502ef8d..052bff816c4d 100644 --- a/sound/soc/tegra/tegra20_spdif.c +++ b/sound/soc/tegra/tegra20_spdif.c @@ -36,105 +36,105 @@ #include "tegra20_spdif.h" -#define DRV_NAME "tegra-spdif" +#define DRV_NAME "tegra20-spdif" -static inline void tegra_spdif_write(struct tegra_spdif *spdif, u32 reg, +static inline void tegra20_spdif_write(struct tegra20_spdif *spdif, u32 reg, u32 val) { __raw_writel(val, spdif->regs + reg); } -static inline u32 tegra_spdif_read(struct tegra_spdif *spdif, u32 reg) +static inline u32 tegra20_spdif_read(struct tegra20_spdif *spdif, u32 reg) { return __raw_readl(spdif->regs + reg); } #ifdef CONFIG_DEBUG_FS -static int tegra_spdif_show(struct seq_file *s, void *unused) +static int tegra20_spdif_show(struct seq_file *s, void *unused) { #define REG(r) { r, #r } static const struct { int offset; const char *name; } regs[] = { - REG(TEGRA_SPDIF_CTRL), - REG(TEGRA_SPDIF_STATUS), - REG(TEGRA_SPDIF_STROBE_CTRL), - REG(TEGRA_SPDIF_DATA_FIFO_CSR), - REG(TEGRA_SPDIF_CH_STA_RX_A), - REG(TEGRA_SPDIF_CH_STA_RX_B), - REG(TEGRA_SPDIF_CH_STA_RX_C), - REG(TEGRA_SPDIF_CH_STA_RX_D), - REG(TEGRA_SPDIF_CH_STA_RX_E), - REG(TEGRA_SPDIF_CH_STA_RX_F), - REG(TEGRA_SPDIF_CH_STA_TX_A), - REG(TEGRA_SPDIF_CH_STA_TX_B), - REG(TEGRA_SPDIF_CH_STA_TX_C), - REG(TEGRA_SPDIF_CH_STA_TX_D), - REG(TEGRA_SPDIF_CH_STA_TX_E), - REG(TEGRA_SPDIF_CH_STA_TX_F), + REG(TEGRA20_SPDIF_CTRL), + REG(TEGRA20_SPDIF_STATUS), + REG(TEGRA20_SPDIF_STROBE_CTRL), + REG(TEGRA20_SPDIF_DATA_FIFO_CSR), + REG(TEGRA20_SPDIF_CH_STA_RX_A), + REG(TEGRA20_SPDIF_CH_STA_RX_B), + REG(TEGRA20_SPDIF_CH_STA_RX_C), + REG(TEGRA20_SPDIF_CH_STA_RX_D), + REG(TEGRA20_SPDIF_CH_STA_RX_E), + REG(TEGRA20_SPDIF_CH_STA_RX_F), + REG(TEGRA20_SPDIF_CH_STA_TX_A), + REG(TEGRA20_SPDIF_CH_STA_TX_B), + REG(TEGRA20_SPDIF_CH_STA_TX_C), + REG(TEGRA20_SPDIF_CH_STA_TX_D), + REG(TEGRA20_SPDIF_CH_STA_TX_E), + REG(TEGRA20_SPDIF_CH_STA_TX_F), }; #undef REG - struct tegra_spdif *spdif = s->private; + struct tegra20_spdif *spdif = s->private; int i; for (i = 0; i < ARRAY_SIZE(regs); i++) { - u32 val = tegra_spdif_read(spdif, regs[i].offset); + u32 val = tegra20_spdif_read(spdif, regs[i].offset); seq_printf(s, "%s = %08x\n", regs[i].name, val); } return 0; } -static int tegra_spdif_debug_open(struct inode *inode, struct file *file) +static int tegra20_spdif_debug_open(struct inode *inode, struct file *file) { - return single_open(file, tegra_spdif_show, inode->i_private); + return single_open(file, tegra20_spdif_show, inode->i_private); } -static const struct file_operations tegra_spdif_debug_fops = { - .open = tegra_spdif_debug_open, +static const struct file_operations tegra20_spdif_debug_fops = { + .open = tegra20_spdif_debug_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; -static void tegra_spdif_debug_add(struct tegra_spdif *spdif) +static void tegra20_spdif_debug_add(struct tegra20_spdif *spdif) { spdif->debug = debugfs_create_file(DRV_NAME, S_IRUGO, snd_soc_debugfs_root, spdif, - &tegra_spdif_debug_fops); + &tegra20_spdif_debug_fops); } -static void tegra_spdif_debug_remove(struct tegra_spdif *spdif) +static void tegra20_spdif_debug_remove(struct tegra20_spdif *spdif) { if (spdif->debug) debugfs_remove(spdif->debug); } #else -static inline void tegra_spdif_debug_add(struct tegra_spdif *spdif) +static inline void tegra20_spdif_debug_add(struct tegra20_spdif *spdif) { } -static inline void tegra_spdif_debug_remove(struct tegra_spdif *spdif) +static inline void tegra20_spdif_debug_remove(struct tegra20_spdif *spdif) { } #endif -static int tegra_spdif_hw_params(struct snd_pcm_substream *substream, +static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { struct device *dev = substream->pcm->card->dev; - struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); int ret, spdifclock; - spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_PACK; - spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_BIT_MODE_MASK; + spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_PACK; + spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_BIT_MODE_MASK; switch (params_format(params)) { case SNDRV_PCM_FORMAT_S16_LE: - spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_PACK; - spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_BIT_MODE_16BIT; + spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_PACK; + spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT; break; default: return -EINVAL; @@ -175,34 +175,34 @@ static int tegra_spdif_hw_params(struct snd_pcm_substream *substream, return 0; } -static void tegra_spdif_start_playback(struct tegra_spdif *spdif) +static void tegra20_spdif_start_playback(struct tegra20_spdif *spdif) { - spdif->reg_ctrl |= TEGRA_SPDIF_CTRL_TX_EN; - tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl); + spdif->reg_ctrl |= TEGRA20_SPDIF_CTRL_TX_EN; + tegra20_spdif_write(spdif, TEGRA20_SPDIF_CTRL, spdif->reg_ctrl); } -static void tegra_spdif_stop_playback(struct tegra_spdif *spdif) +static void tegra20_spdif_stop_playback(struct tegra20_spdif *spdif) { - spdif->reg_ctrl &= ~TEGRA_SPDIF_CTRL_TX_EN; - tegra_spdif_write(spdif, TEGRA_SPDIF_CTRL, spdif->reg_ctrl); + spdif->reg_ctrl &= ~TEGRA20_SPDIF_CTRL_TX_EN; + tegra20_spdif_write(spdif, TEGRA20_SPDIF_CTRL, spdif->reg_ctrl); } -static int tegra_spdif_trigger(struct snd_pcm_substream *substream, int cmd, +static int tegra20_spdif_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_dai *dai) { - struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); switch (cmd) { case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: clk_enable(spdif->clk_spdif_out); - tegra_spdif_start_playback(spdif); + tegra20_spdif_start_playback(spdif); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: - tegra_spdif_stop_playback(spdif); + tegra20_spdif_stop_playback(spdif); clk_disable(spdif->clk_spdif_out); break; default: @@ -212,9 +212,9 @@ static int tegra_spdif_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -static int tegra_spdif_probe(struct snd_soc_dai *dai) +static int tegra20_spdif_probe(struct snd_soc_dai *dai) { - struct tegra_spdif *spdif = snd_soc_dai_get_drvdata(dai); + struct tegra20_spdif *spdif = snd_soc_dai_get_drvdata(dai); dai->capture_dma_data = NULL; dai->playback_dma_data = &spdif->playback_dma_data; @@ -222,14 +222,14 @@ static int tegra_spdif_probe(struct snd_soc_dai *dai) return 0; } -static const struct snd_soc_dai_ops tegra_spdif_dai_ops = { - .hw_params = tegra_spdif_hw_params, - .trigger = tegra_spdif_trigger, +static const struct snd_soc_dai_ops tegra20_spdif_dai_ops = { + .hw_params = tegra20_spdif_hw_params, + .trigger = tegra20_spdif_trigger, }; -static struct snd_soc_dai_driver tegra_spdif_dai = { +static struct snd_soc_dai_driver tegra20_spdif_dai = { .name = DRV_NAME, - .probe = tegra_spdif_probe, + .probe = tegra20_spdif_probe, .playback = { .channels_min = 2, .channels_max = 2, @@ -237,18 +237,18 @@ static struct snd_soc_dai_driver tegra_spdif_dai = { SNDRV_PCM_RATE_48000, .formats = SNDRV_PCM_FMTBIT_S16_LE, }, - .ops = &tegra_spdif_dai_ops, + .ops = &tegra20_spdif_dai_ops, }; -static __devinit int tegra_spdif_platform_probe(struct platform_device *pdev) +static __devinit int tegra20_spdif_platform_probe(struct platform_device *pdev) { - struct tegra_spdif *spdif; + struct tegra20_spdif *spdif; struct resource *mem, *memregion, *dmareq; int ret; - spdif = kzalloc(sizeof(struct tegra_spdif), GFP_KERNEL); + spdif = kzalloc(sizeof(struct tegra20_spdif), GFP_KERNEL); if (!spdif) { - dev_err(&pdev->dev, "Can't allocate tegra_spdif\n"); + dev_err(&pdev->dev, "Can't allocate tegra20_spdif\n"); ret = -ENOMEM; goto exit; } @@ -290,12 +290,12 @@ static __devinit int tegra_spdif_platform_probe(struct platform_device *pdev) goto err_release; } - spdif->playback_dma_data.addr = mem->start + TEGRA_SPDIF_DATA_OUT; + spdif->playback_dma_data.addr = mem->start + TEGRA20_SPDIF_DATA_OUT; spdif->playback_dma_data.wrap = 4; spdif->playback_dma_data.width = 32; spdif->playback_dma_data.req_sel = dmareq->start; - ret = snd_soc_register_dai(&pdev->dev, &tegra_spdif_dai); + ret = snd_soc_register_dai(&pdev->dev, &tegra20_spdif_dai); if (ret) { dev_err(&pdev->dev, "Could not register DAI: %d\n", ret); ret = -ENOMEM; @@ -308,7 +308,7 @@ static __devinit int tegra_spdif_platform_probe(struct platform_device *pdev) goto err_unregister_dai; } - tegra_spdif_debug_add(spdif); + tegra20_spdif_debug_add(spdif); return 0; @@ -326,15 +326,15 @@ exit: return ret; } -static int __devexit tegra_spdif_platform_remove(struct platform_device *pdev) +static int __devexit tegra20_spdif_platform_remove(struct platform_device *pdev) { - struct tegra_spdif *spdif = dev_get_drvdata(&pdev->dev); + struct tegra20_spdif *spdif = dev_get_drvdata(&pdev->dev); struct resource *res; tegra_pcm_platform_unregister(&pdev->dev); snd_soc_unregister_dai(&pdev->dev); - tegra_spdif_debug_remove(spdif); + tegra20_spdif_debug_remove(spdif); iounmap(spdif->regs); @@ -348,18 +348,18 @@ static int __devexit tegra_spdif_platform_remove(struct platform_device *pdev) return 0; } -static struct platform_driver tegra_spdif_driver = { +static struct platform_driver tegra20_spdif_driver = { .driver = { .name = DRV_NAME, .owner = THIS_MODULE, }, - .probe = tegra_spdif_platform_probe, - .remove = __devexit_p(tegra_spdif_platform_remove), + .probe = tegra20_spdif_platform_probe, + .remove = __devexit_p(tegra20_spdif_platform_remove), }; -module_platform_driver(tegra_spdif_driver); +module_platform_driver(tegra20_spdif_driver); MODULE_AUTHOR("Stephen Warren "); -MODULE_DESCRIPTION("Tegra SPDIF ASoC driver"); +MODULE_DESCRIPTION("Tegra20 SPDIF ASoC driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/tegra/tegra20_spdif.h b/sound/soc/tegra/tegra20_spdif.h index 001f9dcd2033..823af4c6bb7c 100644 --- a/sound/soc/tegra/tegra20_spdif.h +++ b/sound/soc/tegra/tegra20_spdif.h @@ -23,83 +23,83 @@ * */ -#ifndef __TEGRA_SPDIF_H__ -#define __TEGRA_SPDIF_H__ +#ifndef __TEGRA20_SPDIF_H__ +#define __TEGRA20_SPDIF_H__ #include "tegra_pcm.h" -/* Offsets from TEGRA_SPDIF_BASE */ - -#define TEGRA_SPDIF_CTRL 0x0 -#define TEGRA_SPDIF_STATUS 0x4 -#define TEGRA_SPDIF_STROBE_CTRL 0x8 -#define TEGRA_SPDIF_DATA_FIFO_CSR 0x0C -#define TEGRA_SPDIF_DATA_OUT 0x40 -#define TEGRA_SPDIF_DATA_IN 0x80 -#define TEGRA_SPDIF_CH_STA_RX_A 0x100 -#define TEGRA_SPDIF_CH_STA_RX_B 0x104 -#define TEGRA_SPDIF_CH_STA_RX_C 0x108 -#define TEGRA_SPDIF_CH_STA_RX_D 0x10C -#define TEGRA_SPDIF_CH_STA_RX_E 0x110 -#define TEGRA_SPDIF_CH_STA_RX_F 0x114 -#define TEGRA_SPDIF_CH_STA_TX_A 0x140 -#define TEGRA_SPDIF_CH_STA_TX_B 0x144 -#define TEGRA_SPDIF_CH_STA_TX_C 0x148 -#define TEGRA_SPDIF_CH_STA_TX_D 0x14C -#define TEGRA_SPDIF_CH_STA_TX_E 0x150 -#define TEGRA_SPDIF_CH_STA_TX_F 0x154 -#define TEGRA_SPDIF_USR_STA_RX_A 0x180 -#define TEGRA_SPDIF_USR_DAT_TX_A 0x1C0 - -/* Fields in TEGRA_SPDIF_CTRL */ +/* Offsets from TEGRA20_SPDIF_BASE */ + +#define TEGRA20_SPDIF_CTRL 0x0 +#define TEGRA20_SPDIF_STATUS 0x4 +#define TEGRA20_SPDIF_STROBE_CTRL 0x8 +#define TEGRA20_SPDIF_DATA_FIFO_CSR 0x0C +#define TEGRA20_SPDIF_DATA_OUT 0x40 +#define TEGRA20_SPDIF_DATA_IN 0x80 +#define TEGRA20_SPDIF_CH_STA_RX_A 0x100 +#define TEGRA20_SPDIF_CH_STA_RX_B 0x104 +#define TEGRA20_SPDIF_CH_STA_RX_C 0x108 +#define TEGRA20_SPDIF_CH_STA_RX_D 0x10C +#define TEGRA20_SPDIF_CH_STA_RX_E 0x110 +#define TEGRA20_SPDIF_CH_STA_RX_F 0x114 +#define TEGRA20_SPDIF_CH_STA_TX_A 0x140 +#define TEGRA20_SPDIF_CH_STA_TX_B 0x144 +#define TEGRA20_SPDIF_CH_STA_TX_C 0x148 +#define TEGRA20_SPDIF_CH_STA_TX_D 0x14C +#define TEGRA20_SPDIF_CH_STA_TX_E 0x150 +#define TEGRA20_SPDIF_CH_STA_TX_F 0x154 +#define TEGRA20_SPDIF_USR_STA_RX_A 0x180 +#define TEGRA20_SPDIF_USR_DAT_TX_A 0x1C0 + +/* Fields in TEGRA20_SPDIF_CTRL */ /* Start capturing from 0=right, 1=left channel */ -#define TEGRA_SPDIF_CTRL_CAP_LC (1 << 30) +#define TEGRA20_SPDIF_CTRL_CAP_LC (1 << 30) /* SPDIF receiver(RX) enable */ -#define TEGRA_SPDIF_CTRL_RX_EN (1 << 29) +#define TEGRA20_SPDIF_CTRL_RX_EN (1 << 29) /* SPDIF Transmitter(TX) enable */ -#define TEGRA_SPDIF_CTRL_TX_EN (1 << 28) +#define TEGRA20_SPDIF_CTRL_TX_EN (1 << 28) /* Transmit Channel status */ -#define TEGRA_SPDIF_CTRL_TC_EN (1 << 27) +#define TEGRA20_SPDIF_CTRL_TC_EN (1 << 27) /* Transmit user Data */ -#define TEGRA_SPDIF_CTRL_TU_EN (1 << 26) +#define TEGRA20_SPDIF_CTRL_TU_EN (1 << 26) /* Interrupt on transmit error */ -#define TEGRA_SPDIF_CTRL_IE_TXE (1 << 25) +#define TEGRA20_SPDIF_CTRL_IE_TXE (1 << 25) /* Interrupt on receive error */ -#define TEGRA_SPDIF_CTRL_IE_RXE (1 << 24) +#define TEGRA20_SPDIF_CTRL_IE_RXE (1 << 24) /* Interrupt on invalid preamble */ -#define TEGRA_SPDIF_CTRL_IE_P (1 << 23) +#define TEGRA20_SPDIF_CTRL_IE_P (1 << 23) /* Interrupt on "B" preamble */ -#define TEGRA_SPDIF_CTRL_IE_B (1 << 22) +#define TEGRA20_SPDIF_CTRL_IE_B (1 << 22) /* Interrupt when block of channel status received */ -#define TEGRA_SPDIF_CTRL_IE_C (1 << 21) +#define TEGRA20_SPDIF_CTRL_IE_C (1 << 21) /* Interrupt when a valid information unit (IU) is received */ -#define TEGRA_SPDIF_CTRL_IE_U (1 << 20) +#define TEGRA20_SPDIF_CTRL_IE_U (1 << 20) /* Interrupt when RX user FIFO attention level is reached */ -#define TEGRA_SPDIF_CTRL_QE_RU (1 << 19) +#define TEGRA20_SPDIF_CTRL_QE_RU (1 << 19) /* Interrupt when TX user FIFO attention level is reached */ -#define TEGRA_SPDIF_CTRL_QE_TU (1 << 18) +#define TEGRA20_SPDIF_CTRL_QE_TU (1 << 18) /* Interrupt when RX data FIFO attention level is reached */ -#define TEGRA_SPDIF_CTRL_QE_RX (1 << 17) +#define TEGRA20_SPDIF_CTRL_QE_RX (1 << 17) /* Interrupt when TX data FIFO attention level is reached */ -#define TEGRA_SPDIF_CTRL_QE_TX (1 << 16) +#define TEGRA20_SPDIF_CTRL_QE_TX (1 << 16) /* Loopback test mode enable */ -#define TEGRA_SPDIF_CTRL_LBK_EN (1 << 15) +#define TEGRA20_SPDIF_CTRL_LBK_EN (1 << 15) /* * Pack data mode: @@ -107,7 +107,7 @@ * interface data bit size). * 1 = Packeted left/right channel data into a single word. */ -#define TEGRA_SPDIF_CTRL_PACK (1 << 14) +#define TEGRA20_SPDIF_CTRL_PACK (1 << 14) /* * 00 = 16bit data @@ -115,19 +115,19 @@ * 10 = 24bit data * 11 = raw data */ -#define TEGRA_SPDIF_BIT_MODE_16BIT 0 -#define TEGRA_SPDIF_BIT_MODE_20BIT 1 -#define TEGRA_SPDIF_BIT_MODE_24BIT 2 -#define TEGRA_SPDIF_BIT_MODE_RAW 3 +#define TEGRA20_SPDIF_BIT_MODE_16BIT 0 +#define TEGRA20_SPDIF_BIT_MODE_20BIT 1 +#define TEGRA20_SPDIF_BIT_MODE_24BIT 2 +#define TEGRA20_SPDIF_BIT_MODE_RAW 3 -#define TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT 12 -#define TEGRA_SPDIF_CTRL_BIT_MODE_MASK (3 << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) -#define TEGRA_SPDIF_CTRL_BIT_MODE_16BIT (TEGRA_SPDIF_BIT_MODE_16BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) -#define TEGRA_SPDIF_CTRL_BIT_MODE_20BIT (TEGRA_SPDIF_BIT_MODE_20BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) -#define TEGRA_SPDIF_CTRL_BIT_MODE_24BIT (TEGRA_SPDIF_BIT_MODE_24BIT << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) -#define TEGRA_SPDIF_CTRL_BIT_MODE_RAW (TEGRA_SPDIF_BIT_MODE_RAW << TEGRA_SPDIF_CTRL_BIT_MODE_SHIFT) +#define TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT 12 +#define TEGRA20_SPDIF_CTRL_BIT_MODE_MASK (3 << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT) +#define TEGRA20_SPDIF_CTRL_BIT_MODE_16BIT (TEGRA20_SPDIF_BIT_MODE_16BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT) +#define TEGRA20_SPDIF_CTRL_BIT_MODE_20BIT (TEGRA20_SPDIF_BIT_MODE_20BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT) +#define TEGRA20_SPDIF_CTRL_BIT_MODE_24BIT (TEGRA20_SPDIF_BIT_MODE_24BIT << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT) +#define TEGRA20_SPDIF_CTRL_BIT_MODE_RAW (TEGRA20_SPDIF_BIT_MODE_RAW << TEGRA20_SPDIF_CTRL_BIT_MODE_SHIFT) -/* Fields in TEGRA_SPDIF_STATUS */ +/* Fields in TEGRA20_SPDIF_STATUS */ /* * Note: IS_P, IS_B, IS_C, and IS_U are sticky bits. Software must @@ -142,7 +142,7 @@ * (a) the end of a frame is reached after RX_EN is deeasserted, or * (b) the SPDIF data stream becomes inactive. */ -#define TEGRA_SPDIF_STATUS_RX_BSY (1 << 29) +#define TEGRA20_SPDIF_STATUS_RX_BSY (1 << 29) /* * Transmitter(TX) shifter is busy transmitting data. @@ -150,7 +150,7 @@ * This bit is deasserted when the end of a frame is reached after * TX_EN is deasserted. */ -#define TEGRA_SPDIF_STATUS_TX_BSY (1 << 28) +#define TEGRA20_SPDIF_STATUS_TX_BSY (1 << 28) /* * TX is busy shifting out channel status. @@ -160,7 +160,7 @@ * (a) the end of a frame is reached after TX_EN is deasserted, or * (b) CH_STA_TX_F register is loaded into the internal shifter. */ -#define TEGRA_SPDIF_STATUS_TC_BSY (1 << 27) +#define TEGRA20_SPDIF_STATUS_TC_BSY (1 << 27) /* * TX User data FIFO busy. @@ -169,173 +169,173 @@ * (a) the end of a frame is reached after TX_EN is deasserted, or * (b) there's no data left in the TX user FIFO. */ -#define TEGRA_SPDIF_STATUS_TU_BSY (1 << 26) +#define TEGRA20_SPDIF_STATUS_TU_BSY (1 << 26) /* TX FIFO Underrun error status */ -#define TEGRA_SPDIF_STATUS_TX_ERR (1 << 25) +#define TEGRA20_SPDIF_STATUS_TX_ERR (1 << 25) /* RX FIFO Overrun error status */ -#define TEGRA_SPDIF_STATUS_RX_ERR (1 << 24) +#define TEGRA20_SPDIF_STATUS_RX_ERR (1 << 24) /* Preamble status: 0=Preamble OK, 1=bad/missing preamble */ -#define TEGRA_SPDIF_STATUS_IS_P (1 << 23) +#define TEGRA20_SPDIF_STATUS_IS_P (1 << 23) /* B-preamble detection status: 0=not detected, 1=B-preamble detected */ -#define TEGRA_SPDIF_STATUS_IS_B (1 << 22) +#define TEGRA20_SPDIF_STATUS_IS_B (1 << 22) /* * RX channel block data receive status: * 0=entire block not recieved yet. * 1=received entire block of channel status, */ -#define TEGRA_SPDIF_STATUS_IS_C (1 << 21) +#define TEGRA20_SPDIF_STATUS_IS_C (1 << 21) /* RX User Data Valid flag: 1=valid IU detected, 0 = no IU detected. */ -#define TEGRA_SPDIF_STATUS_IS_U (1 << 20) +#define TEGRA20_SPDIF_STATUS_IS_U (1 << 20) /* * RX User FIFO Status: * 1=attention level reached, 0=attention level not reached. */ -#define TEGRA_SPDIF_STATUS_QS_RU (1 << 19) +#define TEGRA20_SPDIF_STATUS_QS_RU (1 << 19) /* * TX User FIFO Status: * 1=attention level reached, 0=attention level not reached. */ -#define TEGRA_SPDIF_STATUS_QS_TU (1 << 18) +#define TEGRA20_SPDIF_STATUS_QS_TU (1 << 18) /* * RX Data FIFO Status: * 1=attention level reached, 0=attention level not reached. */ -#define TEGRA_SPDIF_STATUS_QS_RX (1 << 17) +#define TEGRA20_SPDIF_STATUS_QS_RX (1 << 17) /* * TX Data FIFO Status: * 1=attention level reached, 0=attention level not reached. */ -#define TEGRA_SPDIF_STATUS_QS_TX (1 << 16) +#define TEGRA20_SPDIF_STATUS_QS_TX (1 << 16) -/* Fields in TEGRA_SPDIF_STROBE_CTRL */ +/* Fields in TEGRA20_SPDIF_STROBE_CTRL */ /* * Indicates the approximate number of detected SPDIFIN clocks within a * bi-phase period. */ -#define TEGRA_SPDIF_STROBE_CTRL_PERIOD_SHIFT 16 -#define TEGRA_SPDIF_STROBE_CTRL_PERIOD_MASK (0xff << TEGRA_SPDIF_STROBE_CTRL_PERIOD_SHIFT) +#define TEGRA20_SPDIF_STROBE_CTRL_PERIOD_SHIFT 16 +#define TEGRA20_SPDIF_STROBE_CTRL_PERIOD_MASK (0xff << TEGRA20_SPDIF_STROBE_CTRL_PERIOD_SHIFT) /* Data strobe mode: 0=Auto-locked 1=Manual locked */ -#define TEGRA_SPDIF_STROBE_CTRL_STROBE (1 << 15) +#define TEGRA20_SPDIF_STROBE_CTRL_STROBE (1 << 15) /* * Manual data strobe time within the bi-phase clock period (in terms of * the number of over-sampling clocks). */ -#define TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT 8 -#define TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_MASK (0x1f << TEGRA_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT) +#define TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT 8 +#define TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_MASK (0x1f << TEGRA20_SPDIF_STROBE_CTRL_DATA_STROBES_SHIFT) /* * Manual SPDIFIN bi-phase clock period (in terms of the number of * over-sampling clocks). */ -#define TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT 0 -#define TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_MASK (0x3f << TEGRA_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT) +#define TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT 0 +#define TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_MASK (0x3f << TEGRA20_SPDIF_STROBE_CTRL_CLOCK_PERIOD_SHIFT) /* Fields in SPDIF_DATA_FIFO_CSR */ /* Clear Receiver User FIFO (RX USR.FIFO) */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_CLR (1 << 31) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_CLR (1 << 31) -#define TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT 0 -#define TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS 1 -#define TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS 2 -#define TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS 3 +#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT 0 +#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS 1 +#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS 2 +#define TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS 3 /* RU FIFO attention level */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT 29 -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_MASK \ - (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU1_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU2_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU3_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU4_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT 29 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_MASK \ + (0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU1_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU2_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU3_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_RU4_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_ATN_LVL_SHIFT) /* Number of RX USR.FIFO levels with valid data. */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT 24 -#define TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_MASK (0x1f << TEGRA_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT 24 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_MASK (0x1f << TEGRA20_SPDIF_DATA_FIFO_CSR_RU_FULL_COUNT_SHIFT) /* Clear Transmitter User FIFO (TX USR.FIFO) */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_CLR (1 << 23) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_CLR (1 << 23) /* TU FIFO attention level */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT 21 -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_MASK \ - (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU1_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU2_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU3_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU4_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT 21 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_MASK \ + (0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU1_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU2_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_TWO_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU3_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_THREE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_TU4_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_U_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TU_ATN_LVL_SHIFT) /* Number of TX USR.FIFO levels that could be filled. */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT 16 -#define TEGRA_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT 16 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TU_EMPTY_COUNT_SHIFT) /* Clear Receiver Data FIFO (RX DATA.FIFO) */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_CLR (1 << 15) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_CLR (1 << 15) -#define TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT 0 -#define TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS 1 -#define TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS 2 -#define TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS 3 +#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT 0 +#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS 1 +#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS 2 +#define TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS 3 /* RU FIFO attention level */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT 13 -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_MASK \ - (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU1_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU4_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU8_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU12_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT 13 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_MASK \ + (0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU1_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU4_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU8_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_RU12_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_ATN_LVL_SHIFT) /* Number of RX DATA.FIFO levels with valid data. */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT 8 -#define TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_MASK (0x1f << TEGRA_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT 8 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_MASK (0x1f << TEGRA20_SPDIF_DATA_FIFO_CSR_RX_FULL_COUNT_SHIFT) /* Clear Transmitter Data FIFO (TX DATA.FIFO) */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_CLR (1 << 7) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_CLR (1 << 7) /* TU FIFO attention level */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT 5 -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK \ - (0x3 << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU1_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU8_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU12_WORD_FULL \ - (TEGRA_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT 5 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_MASK \ + (0x3 << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU1_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_ONE_SLOT << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU4_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_FOUR_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU8_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_EIGHT_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_TU12_WORD_FULL \ + (TEGRA20_SPDIF_FIFO_ATN_LVL_D_TWELVE_SLOTS << TEGRA20_SPDIF_DATA_FIFO_CSR_TX_ATN_LVL_SHIFT) /* Number of TX DATA.FIFO levels that could be filled. */ -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT 0 -#define TEGRA_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT) +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT 0 +#define TEGRA20_SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_MASK (0x1f << SPDIF_DATA_FIFO_CSR_TX_EMPTY_COUNT_SHIFT) -/* Fields in TEGRA_SPDIF_DATA_OUT */ +/* Fields in TEGRA20_SPDIF_DATA_OUT */ /* * This register has 5 different formats: @@ -346,36 +346,36 @@ * 16-bit packed (BIT_MODE=00, PACK=1) */ -#define TEGRA_SPDIF_DATA_OUT_DATA_16_SHIFT 0 -#define TEGRA_SPDIF_DATA_OUT_DATA_16_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_SHIFT) +#define TEGRA20_SPDIF_DATA_OUT_DATA_16_SHIFT 0 +#define TEGRA20_SPDIF_DATA_OUT_DATA_16_MASK (0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_SHIFT) -#define TEGRA_SPDIF_DATA_OUT_DATA_20_SHIFT 0 -#define TEGRA_SPDIF_DATA_OUT_DATA_20_MASK (0xfffff << TEGRA_SPDIF_DATA_OUT_DATA_20_SHIFT) +#define TEGRA20_SPDIF_DATA_OUT_DATA_20_SHIFT 0 +#define TEGRA20_SPDIF_DATA_OUT_DATA_20_MASK (0xfffff << TEGRA20_SPDIF_DATA_OUT_DATA_20_SHIFT) -#define TEGRA_SPDIF_DATA_OUT_DATA_24_SHIFT 0 -#define TEGRA_SPDIF_DATA_OUT_DATA_24_MASK (0xffffff << TEGRA_SPDIF_DATA_OUT_DATA_24_SHIFT) +#define TEGRA20_SPDIF_DATA_OUT_DATA_24_SHIFT 0 +#define TEGRA20_SPDIF_DATA_OUT_DATA_24_MASK (0xffffff << TEGRA20_SPDIF_DATA_OUT_DATA_24_SHIFT) -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_P (1 << 31) -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_C (1 << 30) -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_U (1 << 29) -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_V (1 << 28) +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_P (1 << 31) +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_C (1 << 30) +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_U (1 << 29) +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_V (1 << 28) -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT 8 -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_MASK (0xfffff << TEGRA_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT) +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT 8 +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_MASK (0xfffff << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_DATA_SHIFT) -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT 4 -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_MASK (0xf << TEGRA_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT) +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT 4 +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_MASK (0xf << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_AUX_SHIFT) -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT 0 -#define TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT) +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT 0 +#define TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA20_SPDIF_DATA_OUT_DATA_RAW_PREAMBLE_SHIFT) -#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT 16 -#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT) +#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT 16 +#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_RIGHT_SHIFT) -#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT 0 -#define TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT) +#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT 0 +#define TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA20_SPDIF_DATA_OUT_DATA_16_PACKED_LEFT_SHIFT) -/* Fields in TEGRA_SPDIF_DATA_IN */ +/* Fields in TEGRA20_SPDIF_DATA_IN */ /* * This register has 5 different formats: @@ -388,44 +388,44 @@ * Bits 31:24 are common to all modes except 16-bit packed */ -#define TEGRA_SPDIF_DATA_IN_DATA_P (1 << 31) -#define TEGRA_SPDIF_DATA_IN_DATA_C (1 << 30) -#define TEGRA_SPDIF_DATA_IN_DATA_U (1 << 29) -#define TEGRA_SPDIF_DATA_IN_DATA_V (1 << 28) +#define TEGRA20_SPDIF_DATA_IN_DATA_P (1 << 31) +#define TEGRA20_SPDIF_DATA_IN_DATA_C (1 << 30) +#define TEGRA20_SPDIF_DATA_IN_DATA_U (1 << 29) +#define TEGRA20_SPDIF_DATA_IN_DATA_V (1 << 28) -#define TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT 24 -#define TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT) +#define TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT 24 +#define TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_MASK (0xf << TEGRA20_SPDIF_DATA_IN_DATA_PREAMBLE_SHIFT) -#define TEGRA_SPDIF_DATA_IN_DATA_16_SHIFT 0 -#define TEGRA_SPDIF_DATA_IN_DATA_16_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_SHIFT) +#define TEGRA20_SPDIF_DATA_IN_DATA_16_SHIFT 0 +#define TEGRA20_SPDIF_DATA_IN_DATA_16_MASK (0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_SHIFT) -#define TEGRA_SPDIF_DATA_IN_DATA_20_SHIFT 0 -#define TEGRA_SPDIF_DATA_IN_DATA_20_MASK (0xfffff << TEGRA_SPDIF_DATA_IN_DATA_20_SHIFT) +#define TEGRA20_SPDIF_DATA_IN_DATA_20_SHIFT 0 +#define TEGRA20_SPDIF_DATA_IN_DATA_20_MASK (0xfffff << TEGRA20_SPDIF_DATA_IN_DATA_20_SHIFT) -#define TEGRA_SPDIF_DATA_IN_DATA_24_SHIFT 0 -#define TEGRA_SPDIF_DATA_IN_DATA_24_MASK (0xffffff << TEGRA_SPDIF_DATA_IN_DATA_24_SHIFT) +#define TEGRA20_SPDIF_DATA_IN_DATA_24_SHIFT 0 +#define TEGRA20_SPDIF_DATA_IN_DATA_24_MASK (0xffffff << TEGRA20_SPDIF_DATA_IN_DATA_24_SHIFT) -#define TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT 8 -#define TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_MASK (0xfffff << TEGRA_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT) +#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT 8 +#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_MASK (0xfffff << TEGRA20_SPDIF_DATA_IN_DATA_RAW_DATA_SHIFT) -#define TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT 4 -#define TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT) +#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT 4 +#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_MASK (0xf << TEGRA20_SPDIF_DATA_IN_DATA_RAW_AUX_SHIFT) -#define TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT 0 -#define TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT) +#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT 0 +#define TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_MASK (0xf << TEGRA20_SPDIF_DATA_IN_DATA_RAW_PREAMBLE_SHIFT) -#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT 16 -#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT) +#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT 16 +#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_MASK (0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_RIGHT_SHIFT) -#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT 0 -#define TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT) +#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT 0 +#define TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_MASK (0xffff << TEGRA20_SPDIF_DATA_IN_DATA_16_PACKED_LEFT_SHIFT) -/* Fields in TEGRA_SPDIF_CH_STA_RX_A */ -/* Fields in TEGRA_SPDIF_CH_STA_RX_B */ -/* Fields in TEGRA_SPDIF_CH_STA_RX_C */ -/* Fields in TEGRA_SPDIF_CH_STA_RX_D */ -/* Fields in TEGRA_SPDIF_CH_STA_RX_E */ -/* Fields in TEGRA_SPDIF_CH_STA_RX_F */ +/* Fields in TEGRA20_SPDIF_CH_STA_RX_A */ +/* Fields in TEGRA20_SPDIF_CH_STA_RX_B */ +/* Fields in TEGRA20_SPDIF_CH_STA_RX_C */ +/* Fields in TEGRA20_SPDIF_CH_STA_RX_D */ +/* Fields in TEGRA20_SPDIF_CH_STA_RX_E */ +/* Fields in TEGRA20_SPDIF_CH_STA_RX_F */ /* * The 6-word receive channel data page buffer holds a block (192 frames) of @@ -433,12 +433,12 @@ * bit, and from CH_STA_RX_A to CH_STA_RX_F then back to CH_STA_RX_A. */ -/* Fields in TEGRA_SPDIF_CH_STA_TX_A */ -/* Fields in TEGRA_SPDIF_CH_STA_TX_B */ -/* Fields in TEGRA_SPDIF_CH_STA_TX_C */ -/* Fields in TEGRA_SPDIF_CH_STA_TX_D */ -/* Fields in TEGRA_SPDIF_CH_STA_TX_E */ -/* Fields in TEGRA_SPDIF_CH_STA_TX_F */ +/* Fields in TEGRA20_SPDIF_CH_STA_TX_A */ +/* Fields in TEGRA20_SPDIF_CH_STA_TX_B */ +/* Fields in TEGRA20_SPDIF_CH_STA_TX_C */ +/* Fields in TEGRA20_SPDIF_CH_STA_TX_D */ +/* Fields in TEGRA20_SPDIF_CH_STA_TX_E */ +/* Fields in TEGRA20_SPDIF_CH_STA_TX_F */ /* * The 6-word transmit channel data page buffer holds a block (192 frames) of @@ -446,21 +446,21 @@ * bit, and from CH_STA_TX_A to CH_STA_TX_F then back to CH_STA_TX_A. */ -/* Fields in TEGRA_SPDIF_USR_STA_RX_A */ +/* Fields in TEGRA20_SPDIF_USR_STA_RX_A */ /* * This 4-word deep FIFO receives user FIFO field information. The order of * receive is from LSB to MSB bit. */ -/* Fields in TEGRA_SPDIF_USR_DAT_TX_A */ +/* Fields in TEGRA20_SPDIF_USR_DAT_TX_A */ /* * This 4-word deep FIFO transmits user FIFO field information. The order of * transmission is from LSB to MSB bit. */ -struct tegra_spdif { +struct tegra20_spdif { struct clk *clk_spdif_out; struct tegra_pcm_dma_params capture_dma_data; struct tegra_pcm_dma_params playback_dma_data; diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c index 2f9e9ff771c8..0b0df49d9d33 100644 --- a/sound/soc/tegra/tegra_wm8903.c +++ b/sound/soc/tegra/tegra_wm8903.c @@ -350,8 +350,8 @@ static struct snd_soc_dai_link tegra_wm8903_dai = { .name = "WM8903", .stream_name = "WM8903 PCM", .codec_name = "wm8903.0-001a", - .platform_name = "tegra-i2s.0", - .cpu_dai_name = "tegra-i2s.0", + .platform_name = "tegra20-i2s.0", + .cpu_dai_name = "tegra20-i2s.0", .codec_dai_name = "wm8903-hifi", .init = tegra_wm8903_init, .ops = &tegra_wm8903_ops, diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c index 8884667aff43..0fd115e69a8a 100644 --- a/sound/soc/tegra/trimslice.c +++ b/sound/soc/tegra/trimslice.c @@ -116,8 +116,8 @@ static struct snd_soc_dai_link trimslice_tlv320aic23_dai = { .name = "TLV320AIC23", .stream_name = "AIC23", .codec_name = "tlv320aic23-codec.2-001a", - .platform_name = "tegra-i2s.0", - .cpu_dai_name = "tegra-i2s.0", + .platform_name = "tegra20-i2s.0", + .cpu_dai_name = "tegra20-i2s.0", .codec_dai_name = "tlv320aic23-hifi", .ops = &trimslice_asoc_ops, }; -- cgit v1.2.3 From 3f3aaea29ff7ee2d43b430338427f30ba7f60ff9 Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 30 Mar 2012 11:45:01 -0400 Subject: xen/p2m: Move code around to allow for better re-usage. We are going to be using the early_alloc_p2m (and early_alloc_p2m_middle) code in follow up patches which are not related to setting identity pages. Hence lets move the code out in its own function and rename them as appropiate. Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/p2m.c | 62 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 28 deletions(-) (limited to 'arch') diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c index 1b267e75158d..3cc3afeb09a1 100644 --- a/arch/x86/xen/p2m.c +++ b/arch/x86/xen/p2m.c @@ -499,7 +499,7 @@ static bool alloc_p2m(unsigned long pfn) return true; } -static bool __init __early_alloc_p2m(unsigned long pfn) +static bool __init early_alloc_p2m_middle(unsigned long pfn) { unsigned topidx, mididx, idx; @@ -541,6 +541,36 @@ static bool __init __early_alloc_p2m(unsigned long pfn) } return idx != 0; } + +static bool __init early_alloc_p2m(unsigned long pfn) +{ + unsigned topidx = p2m_top_index(pfn); + unsigned long *mid_mfn_p; + unsigned long **mid; + + mid = p2m_top[topidx]; + mid_mfn_p = p2m_top_mfn_p[topidx]; + if (mid == p2m_mid_missing) { + mid = extend_brk(PAGE_SIZE, PAGE_SIZE); + + p2m_mid_init(mid); + + p2m_top[topidx] = mid; + + BUG_ON(mid_mfn_p != p2m_mid_missing_mfn); + } + /* And the save/restore P2M tables.. */ + if (mid_mfn_p == p2m_mid_missing_mfn) { + mid_mfn_p = extend_brk(PAGE_SIZE, PAGE_SIZE); + p2m_mid_mfn_init(mid_mfn_p); + + p2m_top_mfn_p[topidx] = mid_mfn_p; + p2m_top_mfn[topidx] = virt_to_mfn(mid_mfn_p); + /* Note: we don't set mid_mfn_p[midix] here, + * look in early_alloc_p2m_middle */ + } + return true; +} unsigned long __init set_phys_range_identity(unsigned long pfn_s, unsigned long pfn_e) { @@ -559,35 +589,11 @@ unsigned long __init set_phys_range_identity(unsigned long pfn_s, pfn < ALIGN(pfn_e, (P2M_MID_PER_PAGE * P2M_PER_PAGE)); pfn += P2M_MID_PER_PAGE * P2M_PER_PAGE) { - unsigned topidx = p2m_top_index(pfn); - unsigned long *mid_mfn_p; - unsigned long **mid; - - mid = p2m_top[topidx]; - mid_mfn_p = p2m_top_mfn_p[topidx]; - if (mid == p2m_mid_missing) { - mid = extend_brk(PAGE_SIZE, PAGE_SIZE); - - p2m_mid_init(mid); - - p2m_top[topidx] = mid; - - BUG_ON(mid_mfn_p != p2m_mid_missing_mfn); - } - /* And the save/restore P2M tables.. */ - if (mid_mfn_p == p2m_mid_missing_mfn) { - mid_mfn_p = extend_brk(PAGE_SIZE, PAGE_SIZE); - p2m_mid_mfn_init(mid_mfn_p); - - p2m_top_mfn_p[topidx] = mid_mfn_p; - p2m_top_mfn[topidx] = virt_to_mfn(mid_mfn_p); - /* Note: we don't set mid_mfn_p[midix] here, - * look in __early_alloc_p2m */ - } + WARN_ON(!early_alloc_p2m(pfn)); } - __early_alloc_p2m(pfn_s); - __early_alloc_p2m(pfn_e); + early_alloc_p2m_middle(pfn_s); + early_alloc_p2m_middle(pfn_e); for (pfn = pfn_s; pfn < pfn_e; pfn++) if (!__set_phys_to_machine(pfn, IDENTITY_FRAME(pfn))) -- cgit v1.2.3 From cef4cca551d652b7f69c9d76337c5fae24e069dc Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 30 Mar 2012 14:15:14 -0400 Subject: xen/p2m: Allow alloc_p2m_middle to call reserve_brk depending on argument For identity cases we want to call reserve_brk only on the boundary conditions of the middle P2M (so P2M[x][y][0] = extend_brk). This is to work around identify regions (PCI spaces, gaps in E820) which are not aligned on 2MB regions. However for the case were we want to allocate P2M middle leafs at the early bootup stage, irregardless of this alignment check we need some means of doing that. For that we provide the new argument. Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/p2m.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'arch') diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c index 3cc3afeb09a1..8b3a3958d120 100644 --- a/arch/x86/xen/p2m.c +++ b/arch/x86/xen/p2m.c @@ -499,7 +499,7 @@ static bool alloc_p2m(unsigned long pfn) return true; } -static bool __init early_alloc_p2m_middle(unsigned long pfn) +static bool __init early_alloc_p2m_middle(unsigned long pfn, bool check_boundary) { unsigned topidx, mididx, idx; @@ -508,7 +508,7 @@ static bool __init early_alloc_p2m_middle(unsigned long pfn) idx = p2m_index(pfn); /* Pfff.. No boundary cross-over, lets get out. */ - if (!idx) + if (!idx && check_boundary) return false; WARN(p2m_top[topidx][mididx] == p2m_identity, @@ -531,7 +531,7 @@ static bool __init early_alloc_p2m_middle(unsigned long pfn) p2m_top[topidx][mididx] = p2m; /* For save/restore we need to MFN of the P2M saved */ - + mid_mfn_p = p2m_top_mfn_p[topidx]; WARN(mid_mfn_p[mididx] != virt_to_mfn(p2m_missing), "P2M_TOP_P[%d][%d] != MFN of p2m_missing!\n", @@ -592,8 +592,8 @@ unsigned long __init set_phys_range_identity(unsigned long pfn_s, WARN_ON(!early_alloc_p2m(pfn)); } - early_alloc_p2m_middle(pfn_s); - early_alloc_p2m_middle(pfn_e); + early_alloc_p2m_middle(pfn_s, true); + early_alloc_p2m_middle(pfn_e, true); for (pfn = pfn_s; pfn < pfn_e; pfn++) if (!__set_phys_to_machine(pfn, IDENTITY_FRAME(pfn))) -- cgit v1.2.3 From d5096850b47424fb0f1c6a75b8f7184f7169319a Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 30 Mar 2012 14:16:49 -0400 Subject: xen/p2m: Collapse early_alloc_p2m_middle redundant checks. At the start of the function we were checking for idx != 0 and bailing out. And later calling extend_brk if idx != 0. That is unnecessary so remove that checks. Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/xen/p2m.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'arch') diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c index 8b3a3958d120..952edefcedb3 100644 --- a/arch/x86/xen/p2m.c +++ b/arch/x86/xen/p2m.c @@ -502,6 +502,8 @@ static bool alloc_p2m(unsigned long pfn) static bool __init early_alloc_p2m_middle(unsigned long pfn, bool check_boundary) { unsigned topidx, mididx, idx; + unsigned long *p2m; + unsigned long *mid_mfn_p; topidx = p2m_top_index(pfn); mididx = p2m_mid_index(pfn); @@ -522,24 +524,21 @@ static bool __init early_alloc_p2m_middle(unsigned long pfn, bool check_boundary return false; /* Boundary cross-over for the edges: */ - if (idx) { - unsigned long *p2m = extend_brk(PAGE_SIZE, PAGE_SIZE); - unsigned long *mid_mfn_p; + p2m = extend_brk(PAGE_SIZE, PAGE_SIZE); - p2m_init(p2m); + p2m_init(p2m); - p2m_top[topidx][mididx] = p2m; + p2m_top[topidx][mididx] = p2m; - /* For save/restore we need to MFN of the P2M saved */ + /* For save/restore we need to MFN of the P2M saved */ - mid_mfn_p = p2m_top_mfn_p[topidx]; - WARN(mid_mfn_p[mididx] != virt_to_mfn(p2m_missing), - "P2M_TOP_P[%d][%d] != MFN of p2m_missing!\n", - topidx, mididx); - mid_mfn_p[mididx] = virt_to_mfn(p2m); + mid_mfn_p = p2m_top_mfn_p[topidx]; + WARN(mid_mfn_p[mididx] != virt_to_mfn(p2m_missing), + "P2M_TOP_P[%d][%d] != MFN of p2m_missing!\n", + topidx, mididx); + mid_mfn_p[mididx] = virt_to_mfn(p2m); - } - return idx != 0; + return true; } static bool __init early_alloc_p2m(unsigned long pfn) -- cgit v1.2.3 From 940713bb2ce3033f468a220094a07250a2f69bdd Mon Sep 17 00:00:00 2001 From: Konrad Rzeszutek Wilk Date: Fri, 30 Mar 2012 14:33:14 -0400 Subject: xen/p2m: An early bootup variant of set_phys_to_machine During early bootup we can't use alloc_page, so to allocate leaf pages in the P2M we need to use extend_brk. For that we are utilizing the early_alloc_p2m and early_alloc_p2m_middle functions to do the job for us. This function follows the same logic as set_phys_to_machine. Signed-off-by: Konrad Rzeszutek Wilk --- arch/x86/include/asm/xen/page.h | 1 + arch/x86/xen/p2m.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'arch') diff --git a/arch/x86/include/asm/xen/page.h b/arch/x86/include/asm/xen/page.h index c34f96c2f7a0..93971e841dd5 100644 --- a/arch/x86/include/asm/xen/page.h +++ b/arch/x86/include/asm/xen/page.h @@ -44,6 +44,7 @@ extern unsigned long machine_to_phys_nr; extern unsigned long get_phys_to_machine(unsigned long pfn); extern bool set_phys_to_machine(unsigned long pfn, unsigned long mfn); +extern bool __init early_set_phys_to_machine(unsigned long pfn, unsigned long mfn); extern bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn); extern unsigned long set_phys_range_identity(unsigned long pfn_s, unsigned long pfn_e); diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c index 952edefcedb3..ffd08c414e91 100644 --- a/arch/x86/xen/p2m.c +++ b/arch/x86/xen/p2m.c @@ -570,6 +570,21 @@ static bool __init early_alloc_p2m(unsigned long pfn) } return true; } +bool __init early_set_phys_to_machine(unsigned long pfn, unsigned long mfn) +{ + if (unlikely(!__set_phys_to_machine(pfn, mfn))) { + if (!early_alloc_p2m(pfn)) + return false; + + if (!early_alloc_p2m_middle(pfn, false /* boundary crossover OK!*/)) + return false; + + if (!__set_phys_to_machine(pfn, mfn)) + return false; + } + + return true; +} unsigned long __init set_phys_range_identity(unsigned long pfn_s, unsigned long pfn_e) { -- cgit v1.2.3 From 83c529151ab0d4a813e3f6a3e293fff75d468519 Mon Sep 17 00:00:00 2001 From: "Liu, Jinsong" Date: Tue, 28 Feb 2012 05:15:46 +0000 Subject: KVM: x86: expose Intel cpu new features (HLE, RTM) to guest Intel recently release 2 new features, HLE and RTM. Refer to http://software.intel.com/file/41417. This patch expose them to guest. Signed-off-by: Liu, Jinsong Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- arch/x86/kvm/cpuid.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index 9fed5bedaad6..c2134b881033 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -247,7 +247,8 @@ static int do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function, /* cpuid 7.0.ebx */ const u32 kvm_supported_word9_x86_features = - F(FSGSBASE) | F(BMI1) | F(AVX2) | F(SMEP) | F(BMI2) | F(ERMS); + F(FSGSBASE) | F(BMI1) | F(HLE) | F(AVX2) | F(SMEP) | + F(BMI2) | F(ERMS) | F(RTM); /* all calls to cpuid_count() should be made on the same cpu */ get_cpu(); -- cgit v1.2.3 From 675acb758ab2381c72fe3ceb5c091cbd0879d4dd Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Thu, 8 Mar 2012 18:07:56 +0800 Subject: KVM: SVM: count all irq windows exit Also count the exits of fast-path. Signed-off-by: Jason Wang Acked-by: Joerg Roedel Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- arch/x86/kvm/svm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index e334389e1c75..f3167208562e 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -3240,6 +3240,7 @@ static int interrupt_window_interception(struct vcpu_svm *svm) svm_clear_vintr(svm); svm->vmcb->control.int_ctl &= ~V_IRQ_MASK; mark_dirty(svm->vmcb, VMCB_INTR); + ++svm->vcpu.stat.irq_window_exits; /* * If the user space waits to inject interrupts, exit as soon as * possible @@ -3247,7 +3248,6 @@ static int interrupt_window_interception(struct vcpu_svm *svm) if (!irqchip_in_kernel(svm->vcpu.kvm) && kvm_run->request_interrupt_window && !kvm_cpu_has_interrupt(&svm->vcpu)) { - ++svm->vcpu.stat.irq_window_exits; kvm_run->exit_reason = KVM_EXIT_IRQ_WINDOW_OPEN; return 0; } -- cgit v1.2.3 From b6d33834bd4e8bdf4a199812e31b3e36da53c794 Mon Sep 17 00:00:00 2001 From: Christoffer Dall Date: Thu, 8 Mar 2012 16:44:24 -0500 Subject: KVM: Factor out kvm_vcpu_kick to arch-generic code The kvm_vcpu_kick function performs roughly the same funcitonality on most all architectures, so we shouldn't have separate copies. PowerPC keeps a pointer to interchanging waitqueues on the vcpu_arch structure and to accomodate this special need a __KVM_HAVE_ARCH_VCPU_GET_WQ define and accompanying function kvm_arch_vcpu_wq have been defined. For all other architectures this is a generic inline that just returns &vcpu->wq; Acked-by: Scott Wood Signed-off-by: Christoffer Dall Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- arch/ia64/include/asm/kvm_host.h | 1 + arch/ia64/kvm/kvm-ia64.c | 20 +++++--------------- arch/powerpc/include/asm/kvm_host.h | 6 ++++++ arch/powerpc/kvm/powerpc.c | 21 ++++++--------------- arch/s390/kvm/kvm-s390.c | 8 ++++++++ arch/x86/kvm/x86.c | 16 ++-------------- include/linux/kvm_host.h | 9 +++++++++ virt/kvm/kvm_main.c | 22 ++++++++++++++++++++++ 8 files changed, 59 insertions(+), 44 deletions(-) (limited to 'arch') diff --git a/arch/ia64/include/asm/kvm_host.h b/arch/ia64/include/asm/kvm_host.h index e35b3a84a40b..c4b4bac3d09e 100644 --- a/arch/ia64/include/asm/kvm_host.h +++ b/arch/ia64/include/asm/kvm_host.h @@ -365,6 +365,7 @@ struct thash_cb { }; struct kvm_vcpu_stat { + u32 halt_wakeup; }; struct kvm_vcpu_arch { diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index f5104b7c52cd..9d80ff8d9eff 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -1872,21 +1872,6 @@ void kvm_arch_hardware_unsetup(void) { } -void kvm_vcpu_kick(struct kvm_vcpu *vcpu) -{ - int me; - int cpu = vcpu->cpu; - - if (waitqueue_active(&vcpu->wq)) - wake_up_interruptible(&vcpu->wq); - - me = get_cpu(); - if (cpu != me && (unsigned) cpu < nr_cpu_ids && cpu_online(cpu)) - if (!test_and_set_bit(KVM_REQ_KICK, &vcpu->requests)) - smp_send_reschedule(cpu); - put_cpu(); -} - int kvm_apic_set_irq(struct kvm_vcpu *vcpu, struct kvm_lapic_irq *irq) { return __apic_accept_irq(vcpu, irq->vector); @@ -1956,6 +1941,11 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) (kvm_highest_pending_irq(vcpu) != -1); } +int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) +{ + return (!test_and_set_bit(KVM_REQ_KICK, &vcpu->requests)); +} + int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu, struct kvm_mp_state *mp_state) { diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 52eb9c1f4fe0..889383735e73 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -498,4 +498,10 @@ struct kvm_vcpu_arch { #define KVM_MMIO_REG_QPR 0x0040 #define KVM_MMIO_REG_FQPR 0x0060 +#define __KVM_HAVE_ARCH_VCPU_GET_WQ 1 +static inline wait_queue_head *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu) +{ + return vcpu->arch.wqp; +} + #endif /* __POWERPC_KVM_HOST_H__ */ diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 00d7e345b3fe..b5e9046462fd 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -43,6 +43,11 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *v) v->requests; } +int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) +{ + return 1; +} + int kvmppc_kvm_pv(struct kvm_vcpu *vcpu) { int nr = kvmppc_get_gpr(vcpu, 11); @@ -588,21 +593,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run) return r; } -void kvm_vcpu_kick(struct kvm_vcpu *vcpu) -{ - int me; - int cpu = vcpu->cpu; - - me = get_cpu(); - if (waitqueue_active(vcpu->arch.wqp)) { - wake_up_interruptible(vcpu->arch.wqp); - vcpu->stat.halt_wakeup++; - } else if (cpu != me && cpu != -1) { - smp_send_reschedule(vcpu->cpu); - } - put_cpu(); -} - int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq) { if (irq->irq == KVM_INTERRUPT_UNSET) { @@ -611,6 +601,7 @@ int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq) } kvmppc_core_queue_external(vcpu, irq); + kvm_vcpu_kick(vcpu); return 0; diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 217ce44395a4..d30c8350b949 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -423,6 +423,14 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) return 0; } +int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) +{ + /* kvm common code refers to this, but never calls it */ + BUG(); + return 0; +} + + static int kvm_arch_vcpu_ioctl_initial_reset(struct kvm_vcpu *vcpu) { kvm_s390_vcpu_initial_reset(vcpu); diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 4044ce0bf7c1..511031dcb9cc 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -6403,21 +6403,9 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) kvm_cpu_has_interrupt(vcpu)); } -void kvm_vcpu_kick(struct kvm_vcpu *vcpu) +int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu) { - int me; - int cpu = vcpu->cpu; - - if (waitqueue_active(&vcpu->wq)) { - wake_up_interruptible(&vcpu->wq); - ++vcpu->stat.halt_wakeup; - } - - me = get_cpu(); - if (cpu != me && (unsigned)cpu < nr_cpu_ids && cpu_online(cpu)) - if (kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE) - smp_send_reschedule(cpu); - put_cpu(); + return kvm_vcpu_exiting_guest_mode(vcpu) == IN_GUEST_MODE; } int kvm_arch_interrupt_allowed(struct kvm_vcpu *vcpu) diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 3a2cea616283..5b624e1ff814 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -439,6 +439,7 @@ void mark_page_dirty_in_slot(struct kvm *kvm, struct kvm_memory_slot *memslot, gfn_t gfn); void kvm_vcpu_block(struct kvm_vcpu *vcpu); +void kvm_vcpu_kick(struct kvm_vcpu *vcpu); void kvm_vcpu_on_spin(struct kvm_vcpu *vcpu); void kvm_resched(struct kvm_vcpu *vcpu); void kvm_load_guest_fpu(struct kvm_vcpu *vcpu); @@ -507,6 +508,7 @@ int kvm_arch_hardware_setup(void); void kvm_arch_hardware_unsetup(void); void kvm_arch_check_processor_compat(void *rtn); int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu); +int kvm_arch_vcpu_should_kick(struct kvm_vcpu *vcpu); void kvm_free_physmem(struct kvm *kvm); @@ -522,6 +524,13 @@ static inline void kvm_arch_free_vm(struct kvm *kvm) } #endif +#ifndef __KVM_HAVE_ARCH_VCPU_GET_WQ +static inline wait_queue_head_t *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu) +{ + return &vcpu->wq; +} +#endif + int kvm_arch_init_vm(struct kvm *kvm, unsigned long type); void kvm_arch_destroy_vm(struct kvm *kvm); void kvm_free_all_assigned_devices(struct kvm *kvm); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index a9565e240636..7149a2e65524 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1514,6 +1514,28 @@ void kvm_vcpu_block(struct kvm_vcpu *vcpu) finish_wait(&vcpu->wq, &wait); } +/* + * Kick a sleeping VCPU, or a guest VCPU in guest mode, into host kernel mode. + */ +void kvm_vcpu_kick(struct kvm_vcpu *vcpu) +{ + int me; + int cpu = vcpu->cpu; + wait_queue_head_t *wqp; + + wqp = kvm_arch_vcpu_wq(vcpu); + if (waitqueue_active(wqp)) { + wake_up_interruptible(wqp); + ++vcpu->stat.halt_wakeup; + } + + me = get_cpu(); + if (cpu != me && (unsigned)cpu < nr_cpu_ids && cpu_online(cpu)) + if (kvm_arch_vcpu_should_kick(vcpu)) + smp_send_reschedule(cpu); + put_cpu(); +} + void kvm_resched(struct kvm_vcpu *vcpu) { if (!need_resched()) -- cgit v1.2.3 From 2246f8b56315befa30f3d3d2800e0734c774f70e Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 13 Mar 2012 22:35:01 +0100 Subject: KVM: PPC: Rework wqp conditional code On PowerPC, we sometimes use a waitqueue per core, not per thread, so we can't always use the vcpu internal waitqueue. This code has been generalized by Christoffer Dall recently, but unfortunately broke compilation for PowerPC. At the time the helper function is defined, struct kvm_vcpu is not declared yet, so we can't dereference it. This patch moves all logic into the generic inline function, at which time we have all information necessary. Signed-off-by: Alexander Graf Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/kvm_host.h | 6 +----- include/linux/kvm_host.h | 6 ++++-- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 889383735e73..20ab5b2dbd0f 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -498,10 +498,6 @@ struct kvm_vcpu_arch { #define KVM_MMIO_REG_QPR 0x0040 #define KVM_MMIO_REG_FQPR 0x0060 -#define __KVM_HAVE_ARCH_VCPU_GET_WQ 1 -static inline wait_queue_head *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu) -{ - return vcpu->arch.wqp; -} +#define __KVM_HAVE_ARCH_WQP #endif /* __POWERPC_KVM_HOST_H__ */ diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 5b624e1ff814..5184817e714a 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -524,12 +524,14 @@ static inline void kvm_arch_free_vm(struct kvm *kvm) } #endif -#ifndef __KVM_HAVE_ARCH_VCPU_GET_WQ static inline wait_queue_head_t *kvm_arch_vcpu_wq(struct kvm_vcpu *vcpu) { +#ifdef __KVM_HAVE_ARCH_WQP + return vcpu->arch.wqp; +#else return &vcpu->wq; -} #endif +} int kvm_arch_init_vm(struct kvm *kvm, unsigned long type); void kvm_arch_destroy_vm(struct kvm *kvm); -- cgit v1.2.3 From eae3ee7d8a7c59cf63441dedf28674889f5fc477 Mon Sep 17 00:00:00 2001 From: Eric B Munson Date: Sat, 10 Mar 2012 14:37:25 -0500 Subject: x86: pvclock: Add flag to indicate that a vm was stopped by the host This flag will be used to check if the vm was stopped by the host when a soft lockup was detected. The host will set the flag when it stops the guest. On resume, the guest will check this flag if a soft lockup is detected and skip issuing the warning. Signed-off-by: Eric B Munson Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- arch/x86/include/asm/pvclock-abi.h | 1 + 1 file changed, 1 insertion(+) (limited to 'arch') diff --git a/arch/x86/include/asm/pvclock-abi.h b/arch/x86/include/asm/pvclock-abi.h index 35f2d1948ada..6167fd798188 100644 --- a/arch/x86/include/asm/pvclock-abi.h +++ b/arch/x86/include/asm/pvclock-abi.h @@ -40,5 +40,6 @@ struct pvclock_wall_clock { } __attribute__((__packed__)); #define PVCLOCK_TSC_STABLE_BIT (1 << 0) +#define PVCLOCK_GUEST_STOPPED (1 << 1) #endif /* __ASSEMBLY__ */ #endif /* _ASM_X86_PVCLOCK_ABI_H */ -- cgit v1.2.3 From 3b5d56b9317fa7b5407dff1aa7b115bf6cdbd494 Mon Sep 17 00:00:00 2001 From: Eric B Munson Date: Sat, 10 Mar 2012 14:37:26 -0500 Subject: kvmclock: Add functions to check if the host has stopped the vm When a host stops or suspends a VM it will set a flag to show this. The watchdog will use these functions to determine if a softlockup is real, or the result of a suspended VM. Signed-off-by: Eric B Munson asm-generic changes Acked-by: Arnd Bergmann Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- arch/alpha/include/asm/kvm_para.h | 1 + arch/arm/include/asm/kvm_para.h | 1 + arch/avr32/include/asm/kvm_para.h | 1 + arch/blackfin/include/asm/kvm_para.h | 1 + arch/c6x/include/asm/kvm_para.h | 1 + arch/frv/include/asm/kvm_para.h | 1 + arch/h8300/include/asm/kvm_para.h | 1 + arch/hexagon/include/asm/kvm_para.h | 1 + arch/ia64/include/asm/kvm_para.h | 5 +++++ arch/m68k/include/asm/kvm_para.h | 1 + arch/microblaze/include/asm/kvm_para.h | 1 + arch/mips/include/asm/kvm_para.h | 1 + arch/mn10300/include/asm/kvm_para.h | 1 + arch/openrisc/include/asm/kvm_para.h | 1 + arch/parisc/include/asm/kvm_para.h | 1 + arch/powerpc/include/asm/kvm_para.h | 5 +++++ arch/s390/include/asm/kvm_para.h | 5 +++++ arch/score/include/asm/kvm_para.h | 1 + arch/sh/include/asm/kvm_para.h | 1 + arch/sparc/include/asm/kvm_para.h | 1 + arch/tile/include/asm/kvm_para.h | 1 + arch/um/include/asm/kvm_para.h | 1 + arch/unicore32/include/asm/kvm_para.h | 1 + arch/x86/include/asm/kvm_para.h | 8 ++++++++ arch/x86/kernel/kvmclock.c | 21 +++++++++++++++++++++ arch/xtensa/include/asm/kvm_para.h | 1 + include/asm-generic/kvm_para.h | 14 ++++++++++++++ 27 files changed, 79 insertions(+) create mode 100644 arch/alpha/include/asm/kvm_para.h create mode 100644 arch/arm/include/asm/kvm_para.h create mode 100644 arch/avr32/include/asm/kvm_para.h create mode 100644 arch/blackfin/include/asm/kvm_para.h create mode 100644 arch/c6x/include/asm/kvm_para.h create mode 100644 arch/frv/include/asm/kvm_para.h create mode 100644 arch/h8300/include/asm/kvm_para.h create mode 100644 arch/hexagon/include/asm/kvm_para.h create mode 100644 arch/m68k/include/asm/kvm_para.h create mode 100644 arch/microblaze/include/asm/kvm_para.h create mode 100644 arch/mips/include/asm/kvm_para.h create mode 100644 arch/mn10300/include/asm/kvm_para.h create mode 100644 arch/openrisc/include/asm/kvm_para.h create mode 100644 arch/parisc/include/asm/kvm_para.h create mode 100644 arch/score/include/asm/kvm_para.h create mode 100644 arch/sh/include/asm/kvm_para.h create mode 100644 arch/sparc/include/asm/kvm_para.h create mode 100644 arch/tile/include/asm/kvm_para.h create mode 100644 arch/um/include/asm/kvm_para.h create mode 100644 arch/unicore32/include/asm/kvm_para.h create mode 100644 arch/xtensa/include/asm/kvm_para.h create mode 100644 include/asm-generic/kvm_para.h (limited to 'arch') diff --git a/arch/alpha/include/asm/kvm_para.h b/arch/alpha/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/alpha/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/arm/include/asm/kvm_para.h b/arch/arm/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/arm/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/avr32/include/asm/kvm_para.h b/arch/avr32/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/avr32/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/blackfin/include/asm/kvm_para.h b/arch/blackfin/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/blackfin/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/c6x/include/asm/kvm_para.h b/arch/c6x/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/c6x/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/frv/include/asm/kvm_para.h b/arch/frv/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/frv/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/h8300/include/asm/kvm_para.h b/arch/h8300/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/h8300/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/hexagon/include/asm/kvm_para.h b/arch/hexagon/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/hexagon/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/ia64/include/asm/kvm_para.h b/arch/ia64/include/asm/kvm_para.h index 1588aee781a2..2019cb99335e 100644 --- a/arch/ia64/include/asm/kvm_para.h +++ b/arch/ia64/include/asm/kvm_para.h @@ -26,6 +26,11 @@ static inline unsigned int kvm_arch_para_features(void) return 0; } +static inline bool kvm_check_and_clear_guest_paused(void) +{ + return false; +} + #endif #endif diff --git a/arch/m68k/include/asm/kvm_para.h b/arch/m68k/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/m68k/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/microblaze/include/asm/kvm_para.h b/arch/microblaze/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/microblaze/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/mips/include/asm/kvm_para.h b/arch/mips/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/mips/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/mn10300/include/asm/kvm_para.h b/arch/mn10300/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/mn10300/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/openrisc/include/asm/kvm_para.h b/arch/openrisc/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/openrisc/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/parisc/include/asm/kvm_para.h b/arch/parisc/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/parisc/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/powerpc/include/asm/kvm_para.h b/arch/powerpc/include/asm/kvm_para.h index 7b754e743003..c18916bff689 100644 --- a/arch/powerpc/include/asm/kvm_para.h +++ b/arch/powerpc/include/asm/kvm_para.h @@ -206,6 +206,11 @@ static inline unsigned int kvm_arch_para_features(void) return r; } +static inline bool kvm_check_and_clear_guest_paused(void) +{ + return false; +} + #endif /* __KERNEL__ */ #endif /* __POWERPC_KVM_PARA_H__ */ diff --git a/arch/s390/include/asm/kvm_para.h b/arch/s390/include/asm/kvm_para.h index 6964db226f83..a98832961035 100644 --- a/arch/s390/include/asm/kvm_para.h +++ b/arch/s390/include/asm/kvm_para.h @@ -149,6 +149,11 @@ static inline unsigned int kvm_arch_para_features(void) return 0; } +static inline bool kvm_check_and_clear_guest_paused(void) +{ + return false; +} + #endif #endif /* __S390_KVM_PARA_H */ diff --git a/arch/score/include/asm/kvm_para.h b/arch/score/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/score/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/sh/include/asm/kvm_para.h b/arch/sh/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/sh/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/sparc/include/asm/kvm_para.h b/arch/sparc/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/sparc/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/tile/include/asm/kvm_para.h b/arch/tile/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/tile/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/um/include/asm/kvm_para.h b/arch/um/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/um/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/unicore32/include/asm/kvm_para.h b/arch/unicore32/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/unicore32/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/arch/x86/include/asm/kvm_para.h b/arch/x86/include/asm/kvm_para.h index 734c3767cfac..99c4bbe0cca2 100644 --- a/arch/x86/include/asm/kvm_para.h +++ b/arch/x86/include/asm/kvm_para.h @@ -95,6 +95,14 @@ struct kvm_vcpu_pv_apf_data { extern void kvmclock_init(void); extern int kvm_register_clock(char *txt); +#ifdef CONFIG_KVM_CLOCK +bool kvm_check_and_clear_guest_paused(void); +#else +static inline bool kvm_check_and_clear_guest_paused(void) +{ + return false; +} +#endif /* CONFIG_KVMCLOCK */ /* This instruction is vmcall. On non-VT architectures, it will generate a * trap that we will then rewrite to the appropriate instruction. diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c index f8492da65bfc..4ba090ca689d 100644 --- a/arch/x86/kernel/kvmclock.c +++ b/arch/x86/kernel/kvmclock.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -114,6 +115,26 @@ static void kvm_get_preset_lpj(void) preset_lpj = lpj; } +bool kvm_check_and_clear_guest_paused(void) +{ + bool ret = false; + struct pvclock_vcpu_time_info *src; + + /* + * per_cpu() is safe here because this function is only called from + * timer functions where preemption is already disabled. + */ + WARN_ON(!in_atomic()); + src = &__get_cpu_var(hv_clock); + if ((src->flags & PVCLOCK_GUEST_STOPPED) != 0) { + __this_cpu_and(hv_clock.flags, ~PVCLOCK_GUEST_STOPPED); + ret = true; + } + + return ret; +} +EXPORT_SYMBOL_GPL(kvm_check_and_clear_guest_paused); + static struct clocksource kvm_clock = { .name = "kvm-clock", .read = kvm_clock_get_cycles, diff --git a/arch/xtensa/include/asm/kvm_para.h b/arch/xtensa/include/asm/kvm_para.h new file mode 100644 index 000000000000..14fab8f0b957 --- /dev/null +++ b/arch/xtensa/include/asm/kvm_para.h @@ -0,0 +1 @@ +#include diff --git a/include/asm-generic/kvm_para.h b/include/asm-generic/kvm_para.h new file mode 100644 index 000000000000..05ef7e705939 --- /dev/null +++ b/include/asm-generic/kvm_para.h @@ -0,0 +1,14 @@ +#ifndef _ASM_GENERIC_KVM_PARA_H +#define _ASM_GENERIC_KVM_PARA_H + + +/* + * This function is used by architectures that support kvm to avoid issuing + * false soft lockup messages. + */ +static inline bool kvm_check_and_clear_guest_paused(void) +{ + return false; +} + +#endif -- cgit v1.2.3 From 1c0b28c2a46d98cd258d96b8c222144b22876c46 Mon Sep 17 00:00:00 2001 From: Eric B Munson Date: Sat, 10 Mar 2012 14:37:27 -0500 Subject: KVM: x86: Add ioctl for KVM_KVMCLOCK_CTRL Now that we have a flag that will tell the guest it was suspended, create an interface for that communication using a KVM ioctl. Signed-off-by: Eric B Munson Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- Documentation/virtual/kvm/api.txt | 20 ++++++++++++++++++++ Documentation/virtual/kvm/msr.txt | 4 ++++ arch/x86/kvm/x86.c | 22 ++++++++++++++++++++++ include/linux/kvm.h | 3 +++ 4 files changed, 49 insertions(+) (limited to 'arch') diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index 6386f8c0482e..81ff39f6248d 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -1669,6 +1669,26 @@ at the memory location pointed to by "addr". The list of registers accessible using this interface is identical to the list in 4.64. +4.70 KVM_KVMCLOCK_CTRL + +Capability: KVM_CAP_KVMCLOCK_CTRL +Architectures: Any that implement pvclocks (currently x86 only) +Type: vcpu ioctl +Parameters: None +Returns: 0 on success, -1 on error + +This signals to the host kernel that the specified guest is being paused by +userspace. The host will set a flag in the pvclock structure that is checked +from the soft lockup watchdog. The flag is part of the pvclock structure that +is shared between guest and host, specifically the second bit of the flags +field of the pvclock_vcpu_time_info structure. It will be set exclusively by +the host and read/cleared exclusively by the guest. The guest operation of +checking and clearing the flag must an atomic operation so +load-link/store-conditional, or equivalent must be used. There are two cases +where the guest will clear the flag: when the soft lockup watchdog timer resets +itself or when a soft lockup is detected. This ioctl can be called any time +after pausing the vcpu, but before it is resumed. + 5. The kvm_run structure Application code obtains a pointer to the kvm_run structure by diff --git a/Documentation/virtual/kvm/msr.txt b/Documentation/virtual/kvm/msr.txt index 50317809113d..96b41bd97523 100644 --- a/Documentation/virtual/kvm/msr.txt +++ b/Documentation/virtual/kvm/msr.txt @@ -108,6 +108,10 @@ MSR_KVM_SYSTEM_TIME_NEW: 0x4b564d01 | | time measures taken across 0 | 24 | multiple cpus are guaranteed to | | be monotonic + ------------------------------------------------------------- + | | guest vcpu has been paused by + 1 | N/A | the host + | | See 4.70 in api.txt ------------------------------------------------------------- Availability of this MSR must be checked via bit 3 in 0x4000001 cpuid diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 511031dcb9cc..99b738028fc0 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -2147,6 +2147,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_ASYNC_PF: case KVM_CAP_GET_TSC_KHZ: case KVM_CAP_PCI_2_3: + case KVM_CAP_KVMCLOCK_CTRL: r = 1; break; case KVM_CAP_COALESCED_MMIO: @@ -2597,6 +2598,23 @@ static int kvm_vcpu_ioctl_x86_set_xcrs(struct kvm_vcpu *vcpu, return r; } +/* + * kvm_set_guest_paused() indicates to the guest kernel that it has been + * stopped by the hypervisor. This function will be called from the host only. + * EINVAL is returned when the host attempts to set the flag for a guest that + * does not support pv clocks. + */ +static int kvm_set_guest_paused(struct kvm_vcpu *vcpu) +{ + struct pvclock_vcpu_time_info *src = &vcpu->arch.hv_clock; + if (!vcpu->arch.time_page) + return -EINVAL; + src->flags |= PVCLOCK_GUEST_STOPPED; + mark_page_dirty(vcpu->kvm, vcpu->arch.time >> PAGE_SHIFT); + kvm_make_request(KVM_REQ_CLOCK_UPDATE, vcpu); + return 0; +} + long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { @@ -2873,6 +2891,10 @@ long kvm_arch_vcpu_ioctl(struct file *filp, r = vcpu->arch.virtual_tsc_khz; goto out; } + case KVM_KVMCLOCK_CTRL: { + r = kvm_set_guest_paused(vcpu); + goto out; + } default: r = -EINVAL; } diff --git a/include/linux/kvm.h b/include/linux/kvm.h index 6c322a90b92f..7a9dd4b3dede 100644 --- a/include/linux/kvm.h +++ b/include/linux/kvm.h @@ -589,6 +589,7 @@ struct kvm_ppc_pvinfo { #define KVM_CAP_S390_UCONTROL 73 #define KVM_CAP_SYNC_REGS 74 #define KVM_CAP_PCI_2_3 75 +#define KVM_CAP_KVMCLOCK_CTRL 76 #ifdef KVM_CAP_IRQ_ROUTING @@ -859,6 +860,8 @@ struct kvm_s390_ucas_mapping { /* Available with KVM_CAP_ONE_REG */ #define KVM_GET_ONE_REG _IOW(KVMIO, 0xab, struct kvm_one_reg) #define KVM_SET_ONE_REG _IOW(KVMIO, 0xac, struct kvm_one_reg) +/* VM is being stopped by host */ +#define KVM_KVMCLOCK_CTRL _IO(KVMIO, 0xad) #define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0) #define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1) -- cgit v1.2.3 From 248997095d652576f1213028a95ca5fff85d089f Mon Sep 17 00:00:00 2001 From: Eric B Munson Date: Thu, 15 Mar 2012 18:16:49 -0400 Subject: kvmclock: remove unneeded EXPORT macro check_and_clear_guest_paused does not need to be exported as it isn't used by any modules, remove the export. Signed-off-by: Eric B Munson Signed-off-by: Marcelo Tosatti Signed-off-by: Avi Kivity --- arch/x86/kernel/kvmclock.c | 1 - 1 file changed, 1 deletion(-) (limited to 'arch') diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c index 4ba090ca689d..086eb58c6e80 100644 --- a/arch/x86/kernel/kvmclock.c +++ b/arch/x86/kernel/kvmclock.c @@ -133,7 +133,6 @@ bool kvm_check_and_clear_guest_paused(void) return ret; } -EXPORT_SYMBOL_GPL(kvm_check_and_clear_guest_paused); static struct clocksource kvm_clock = { .name = "kvm-clock", -- cgit v1.2.3 From a0ed46073c14f66dbf0707aaa7588b78da83d7c6 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Thu, 1 Mar 2012 19:31:22 +0900 Subject: KVM: MMU: Split the main body of rmap_write_protect() off from others We will use this in the following patch to implement another function which needs to write protect pages using the rmap information. Note that there is a small change in debug printing for large pages: we do not differentiate them from others to avoid duplicating code. Signed-off-by: Takuya Yoshikawa Signed-off-by: Avi Kivity --- arch/x86/kvm/mmu.c | 53 +++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) (limited to 'arch') diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 4cb164268846..c8b5694d1a48 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -1010,42 +1010,43 @@ static void drop_spte(struct kvm *kvm, u64 *sptep) rmap_remove(kvm, sptep); } -int kvm_mmu_rmap_write_protect(struct kvm *kvm, u64 gfn, - struct kvm_memory_slot *slot) +static int __rmap_write_protect(struct kvm *kvm, unsigned long *rmapp, int level) { - unsigned long *rmapp; - u64 *spte; - int i, write_protected = 0; + u64 *spte = NULL; + int write_protected = 0; - rmapp = __gfn_to_rmap(gfn, PT_PAGE_TABLE_LEVEL, slot); - spte = rmap_next(rmapp, NULL); - while (spte) { + while ((spte = rmap_next(rmapp, spte))) { BUG_ON(!(*spte & PT_PRESENT_MASK)); rmap_printk("rmap_write_protect: spte %p %llx\n", spte, *spte); - if (is_writable_pte(*spte)) { + + if (!is_writable_pte(*spte)) + continue; + + if (level == PT_PAGE_TABLE_LEVEL) { mmu_spte_update(spte, *spte & ~PT_WRITABLE_MASK); - write_protected = 1; + } else { + BUG_ON(!is_large_pte(*spte)); + drop_spte(kvm, spte); + --kvm->stat.lpages; + spte = NULL; } - spte = rmap_next(rmapp, spte); + + write_protected = 1; } - /* check for huge page mappings */ - for (i = PT_DIRECTORY_LEVEL; + return write_protected; +} + +int kvm_mmu_rmap_write_protect(struct kvm *kvm, u64 gfn, + struct kvm_memory_slot *slot) +{ + unsigned long *rmapp; + int i, write_protected = 0; + + for (i = PT_PAGE_TABLE_LEVEL; i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) { rmapp = __gfn_to_rmap(gfn, i, slot); - spte = rmap_next(rmapp, NULL); - while (spte) { - BUG_ON(!(*spte & PT_PRESENT_MASK)); - BUG_ON(!is_large_pte(*spte)); - pgprintk("rmap_write_protect(large): spte %p %llx %lld\n", spte, *spte, gfn); - if (is_writable_pte(*spte)) { - drop_spte(kvm, spte); - --kvm->stat.lpages; - spte = NULL; - write_protected = 1; - } - spte = rmap_next(rmapp, spte); - } + write_protected |= __rmap_write_protect(kvm, rmapp, i); } return write_protected; -- cgit v1.2.3 From 5dc99b2380d59b8aeafa98791f92b96400ed3187 Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Thu, 1 Mar 2012 19:32:16 +0900 Subject: KVM: Avoid checking huge page mappings in get_dirty_log() Dropped such mappings when we enabled dirty logging and we will never create new ones until we stop the logging. For this we introduce a new function which can be used to write protect a range of PT level pages: although we do not need to care about a range of pages at this point, the following patch will need this feature to optimize the write protection of many pages. Signed-off-by: Takuya Yoshikawa Signed-off-by: Avi Kivity --- arch/x86/include/asm/kvm_host.h | 5 +++-- arch/x86/kvm/mmu.c | 40 ++++++++++++++++++++++++++++++---------- arch/x86/kvm/x86.c | 8 +++----- 3 files changed, 36 insertions(+), 17 deletions(-) (limited to 'arch') diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index e216ba066e79..f624ca72ea24 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -712,8 +712,9 @@ void kvm_mmu_set_mask_ptes(u64 user_mask, u64 accessed_mask, int kvm_mmu_reset_context(struct kvm_vcpu *vcpu); void kvm_mmu_slot_remove_write_access(struct kvm *kvm, int slot); -int kvm_mmu_rmap_write_protect(struct kvm *kvm, u64 gfn, - struct kvm_memory_slot *slot); +void kvm_mmu_write_protect_pt_masked(struct kvm *kvm, + struct kvm_memory_slot *slot, + gfn_t gfn_offset, unsigned long mask); void kvm_mmu_zap_all(struct kvm *kvm); unsigned int kvm_mmu_calculate_mmu_pages(struct kvm *kvm); void kvm_mmu_change_mmu_pages(struct kvm *kvm, unsigned int kvm_nr_mmu_pages); diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index c8b5694d1a48..dc5f2459db6c 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -1037,27 +1037,47 @@ static int __rmap_write_protect(struct kvm *kvm, unsigned long *rmapp, int level return write_protected; } -int kvm_mmu_rmap_write_protect(struct kvm *kvm, u64 gfn, - struct kvm_memory_slot *slot) +/** + * kvm_mmu_write_protect_pt_masked - write protect selected PT level pages + * @kvm: kvm instance + * @slot: slot to protect + * @gfn_offset: start of the BITS_PER_LONG pages we care about + * @mask: indicates which pages we should protect + * + * Used when we do not need to care about huge page mappings: e.g. during dirty + * logging we do not have any such mappings. + */ +void kvm_mmu_write_protect_pt_masked(struct kvm *kvm, + struct kvm_memory_slot *slot, + gfn_t gfn_offset, unsigned long mask) { unsigned long *rmapp; - int i, write_protected = 0; - for (i = PT_PAGE_TABLE_LEVEL; - i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) { - rmapp = __gfn_to_rmap(gfn, i, slot); - write_protected |= __rmap_write_protect(kvm, rmapp, i); - } + while (mask) { + rmapp = &slot->rmap[gfn_offset + __ffs(mask)]; + __rmap_write_protect(kvm, rmapp, PT_PAGE_TABLE_LEVEL); - return write_protected; + /* clear the first set bit */ + mask &= mask - 1; + } } static int rmap_write_protect(struct kvm *kvm, u64 gfn) { struct kvm_memory_slot *slot; + unsigned long *rmapp; + int i; + int write_protected = 0; slot = gfn_to_memslot(kvm, gfn); - return kvm_mmu_rmap_write_protect(kvm, gfn, slot); + + for (i = PT_PAGE_TABLE_LEVEL; + i < PT_PAGE_TABLE_LEVEL + KVM_NR_PAGE_SIZES; ++i) { + rmapp = __gfn_to_rmap(gfn, i, slot); + write_protected |= __rmap_write_protect(kvm, rmapp, i); + } + + return write_protected; } static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp, diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 99b738028fc0..813ebf1e55a0 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3095,13 +3095,11 @@ static void write_protect_slot(struct kvm *kvm, /* Not many dirty pages compared to # of shadow pages. */ if (nr_dirty_pages < kvm->arch.n_used_mmu_pages) { - unsigned long gfn_offset; + gfn_t offset; - for_each_set_bit(gfn_offset, dirty_bitmap, memslot->npages) { - unsigned long gfn = memslot->base_gfn + gfn_offset; + for_each_set_bit(offset, dirty_bitmap, memslot->npages) + kvm_mmu_write_protect_pt_masked(kvm, memslot, offset, 1); - kvm_mmu_rmap_write_protect(kvm, gfn, memslot); - } kvm_flush_remote_tlbs(kvm); } else kvm_mmu_slot_remove_write_access(kvm, memslot->id); -- cgit v1.2.3 From 60c34612b70711fb14a8dcbc6a79509902450d2e Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Sat, 3 Mar 2012 14:21:48 +0900 Subject: KVM: Switch to srcu-less get_dirty_log() We have seen some problems of the current implementation of get_dirty_log() which uses synchronize_srcu_expedited() for updating dirty bitmaps; e.g. it is noticeable that this sometimes gives us ms order of latency when we use VGA displays. Furthermore the recent discussion on the following thread "srcu: Implement call_srcu()" http://lkml.org/lkml/2012/1/31/211 also motivated us to implement get_dirty_log() without SRCU. This patch achieves this goal without sacrificing the performance of both VGA and live migration: in practice the new code is much faster than the old one unless we have too many dirty pages. Implementation: The key part of the implementation is the use of xchg() operation for clearing dirty bits atomically. Since this allows us to update only BITS_PER_LONG pages at once, we need to iterate over the dirty bitmap until every dirty bit is cleared again for the next call. Although some people may worry about the problem of using the atomic memory instruction many times to the concurrently accessible bitmap, it is usually accessed with mmu_lock held and we rarely see concurrent accesses: so what we need to care about is the pure xchg() overheads. Another point to note is that we do not use for_each_set_bit() to check which ones in each BITS_PER_LONG pages are actually dirty. Instead we simply use __ffs() in a loop. This is much faster than repeatedly call find_next_bit(). Performance: The dirty-log-perf unit test showed nice improvements, some times faster than before, except for some extreme cases; for such cases the speed of getting dirty page information is much faster than we process it in the userspace. For real workloads, both VGA and live migration, we have observed pure improvements: when the guest was reading a file during live migration, we originally saw a few ms of latency, but with the new method the latency was less than 200us. Signed-off-by: Takuya Yoshikawa Signed-off-by: Avi Kivity --- arch/x86/kvm/x86.c | 116 ++++++++++++++++++++--------------------------------- 1 file changed, 43 insertions(+), 73 deletions(-) (limited to 'arch') diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 813ebf1e55a0..0d9a57875f0b 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -3067,55 +3067,32 @@ static int kvm_vm_ioctl_reinject(struct kvm *kvm, } /** - * write_protect_slot - write protect a slot for dirty logging - * @kvm: the kvm instance - * @memslot: the slot we protect - * @dirty_bitmap: the bitmap indicating which pages are dirty - * @nr_dirty_pages: the number of dirty pages + * kvm_vm_ioctl_get_dirty_log - get and clear the log of dirty pages in a slot + * @kvm: kvm instance + * @log: slot id and address to which we copy the log * - * We have two ways to find all sptes to protect: - * 1. Use kvm_mmu_slot_remove_write_access() which walks all shadow pages and - * checks ones that have a spte mapping a page in the slot. - * 2. Use kvm_mmu_rmap_write_protect() for each gfn found in the bitmap. + * We need to keep it in mind that VCPU threads can write to the bitmap + * concurrently. So, to avoid losing data, we keep the following order for + * each bit: * - * Generally speaking, if there are not so many dirty pages compared to the - * number of shadow pages, we should use the latter. + * 1. Take a snapshot of the bit and clear it if needed. + * 2. Write protect the corresponding page. + * 3. Flush TLB's if needed. + * 4. Copy the snapshot to the userspace. * - * Note that letting others write into a page marked dirty in the old bitmap - * by using the remaining tlb entry is not a problem. That page will become - * write protected again when we flush the tlb and then be reported dirty to - * the user space by copying the old bitmap. + * Between 2 and 3, the guest may write to the page using the remaining TLB + * entry. This is not a problem because the page will be reported dirty at + * step 4 using the snapshot taken before and step 3 ensures that successive + * writes will be logged for the next call. */ -static void write_protect_slot(struct kvm *kvm, - struct kvm_memory_slot *memslot, - unsigned long *dirty_bitmap, - unsigned long nr_dirty_pages) -{ - spin_lock(&kvm->mmu_lock); - - /* Not many dirty pages compared to # of shadow pages. */ - if (nr_dirty_pages < kvm->arch.n_used_mmu_pages) { - gfn_t offset; - - for_each_set_bit(offset, dirty_bitmap, memslot->npages) - kvm_mmu_write_protect_pt_masked(kvm, memslot, offset, 1); - - kvm_flush_remote_tlbs(kvm); - } else - kvm_mmu_slot_remove_write_access(kvm, memslot->id); - - spin_unlock(&kvm->mmu_lock); -} - -/* - * Get (and clear) the dirty memory log for a memory slot. - */ -int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, - struct kvm_dirty_log *log) +int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, struct kvm_dirty_log *log) { int r; struct kvm_memory_slot *memslot; - unsigned long n, nr_dirty_pages; + unsigned long n, i; + unsigned long *dirty_bitmap; + unsigned long *dirty_bitmap_buffer; + bool is_dirty = false; mutex_lock(&kvm->slots_lock); @@ -3124,49 +3101,42 @@ int kvm_vm_ioctl_get_dirty_log(struct kvm *kvm, goto out; memslot = id_to_memslot(kvm->memslots, log->slot); + + dirty_bitmap = memslot->dirty_bitmap; r = -ENOENT; - if (!memslot->dirty_bitmap) + if (!dirty_bitmap) goto out; n = kvm_dirty_bitmap_bytes(memslot); - nr_dirty_pages = memslot->nr_dirty_pages; - /* If nothing is dirty, don't bother messing with page tables. */ - if (nr_dirty_pages) { - struct kvm_memslots *slots, *old_slots; - unsigned long *dirty_bitmap, *dirty_bitmap_head; + dirty_bitmap_buffer = dirty_bitmap + n / sizeof(long); + memset(dirty_bitmap_buffer, 0, n); - dirty_bitmap = memslot->dirty_bitmap; - dirty_bitmap_head = memslot->dirty_bitmap_head; - if (dirty_bitmap == dirty_bitmap_head) - dirty_bitmap_head += n / sizeof(long); - memset(dirty_bitmap_head, 0, n); + spin_lock(&kvm->mmu_lock); - r = -ENOMEM; - slots = kmemdup(kvm->memslots, sizeof(*kvm->memslots), GFP_KERNEL); - if (!slots) - goto out; + for (i = 0; i < n / sizeof(long); i++) { + unsigned long mask; + gfn_t offset; - memslot = id_to_memslot(slots, log->slot); - memslot->nr_dirty_pages = 0; - memslot->dirty_bitmap = dirty_bitmap_head; - update_memslots(slots, NULL); + if (!dirty_bitmap[i]) + continue; - old_slots = kvm->memslots; - rcu_assign_pointer(kvm->memslots, slots); - synchronize_srcu_expedited(&kvm->srcu); - kfree(old_slots); + is_dirty = true; - write_protect_slot(kvm, memslot, dirty_bitmap, nr_dirty_pages); + mask = xchg(&dirty_bitmap[i], 0); + dirty_bitmap_buffer[i] = mask; - r = -EFAULT; - if (copy_to_user(log->dirty_bitmap, dirty_bitmap, n)) - goto out; - } else { - r = -EFAULT; - if (clear_user(log->dirty_bitmap, n)) - goto out; + offset = i * BITS_PER_LONG; + kvm_mmu_write_protect_pt_masked(kvm, memslot, offset, mask); } + if (is_dirty) + kvm_flush_remote_tlbs(kvm); + + spin_unlock(&kvm->mmu_lock); + + r = -EFAULT; + if (copy_to_user(log->dirty_bitmap, dirty_bitmap_buffer, n)) + goto out; r = 0; out: -- cgit v1.2.3 From 52b066fa4e9cbfe45243dd4259b053d3fa7e21f1 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:12 +0000 Subject: powerpc/booke: Set CPU_FTR_DEBUG_LVL_EXC on 32-bit Currently 32-bit only cares about this for choice of exception vector, which is done in core-specific code. However, KVM will want to distinguish as well. Signed-off-by: Scott Wood Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/cputable.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index b9219e99bd2a..3be45cd9aa81 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -376,7 +376,8 @@ extern const char *powerpc_base_platform; #define CPU_FTRS_47X (CPU_FTRS_440x6) #define CPU_FTRS_E200 (CPU_FTR_USE_TB | CPU_FTR_SPE_COMP | \ CPU_FTR_NODSISRALIGN | CPU_FTR_COHERENT_ICACHE | \ - CPU_FTR_UNIFIED_ID_CACHE | CPU_FTR_NOEXECUTE) + CPU_FTR_UNIFIED_ID_CACHE | CPU_FTR_NOEXECUTE | \ + CPU_FTR_DEBUG_LVL_EXC) #define CPU_FTRS_E500 (CPU_FTR_MAYBE_CAN_DOZE | CPU_FTR_USE_TB | \ CPU_FTR_SPE_COMP | CPU_FTR_MAYBE_CAN_NAP | CPU_FTR_NODSISRALIGN | \ CPU_FTR_NOEXECUTE) @@ -385,7 +386,7 @@ extern const char *powerpc_base_platform; CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE) #define CPU_FTRS_E500MC (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | \ CPU_FTR_L2CSR | CPU_FTR_LWSYNC | CPU_FTR_NOEXECUTE | \ - CPU_FTR_DBELL) + CPU_FTR_DBELL | CPU_FTR_DEBUG_LVL_EXC) #define CPU_FTRS_E5500 (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | \ CPU_FTR_L2CSR | CPU_FTR_LWSYNC | CPU_FTR_NOEXECUTE | \ CPU_FTR_DBELL | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ -- cgit v1.2.3 From 06aae86799c1b37f216371e05a1eacb2188bee9d Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:14 +0000 Subject: powerpc/e500: split CPU_FTRS_ALWAYS/CPU_FTRS_POSSIBLE Split e500 (v1/v2) and e500mc/e5500 to allow optimization of feature checks that differ between the two. Signed-off-by: Scott Wood Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/cputable.h | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index 3be45cd9aa81..7108a9c490a3 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -487,8 +487,10 @@ enum { CPU_FTRS_E200 | #endif #ifdef CONFIG_E500 - CPU_FTRS_E500 | CPU_FTRS_E500_2 | CPU_FTRS_E500MC | - CPU_FTRS_E5500 | CPU_FTRS_E6500 | + CPU_FTRS_E500 | CPU_FTRS_E500_2 | +#endif +#ifdef CONFIG_PPC_E500MC + CPU_FTRS_E500MC | CPU_FTRS_E5500 | CPU_FTRS_E6500 | #endif 0, }; @@ -532,8 +534,10 @@ enum { CPU_FTRS_E200 & #endif #ifdef CONFIG_E500 - CPU_FTRS_E500 & CPU_FTRS_E500_2 & CPU_FTRS_E500MC & - CPU_FTRS_E5500 & CPU_FTRS_E6500 & + CPU_FTRS_E500 & CPU_FTRS_E500_2 & +#endif +#ifdef CONFIG_PPC_E500MC + CPU_FTRS_E500MC & CPU_FTRS_E5500 & CPU_FTRS_E6500 & #endif CPU_FTRS_POSSIBLE, }; -- cgit v1.2.3 From 043cc4d724da6bb9e4f417c735accec58dfa40bf Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:20 +0000 Subject: KVM: PPC: factor out lpid allocator from book3s_64_mmu_hv We'll use it on e500mc as well. Signed-off-by: Scott Wood Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/kvm_book3s.h | 3 +++ arch/powerpc/include/asm/kvm_booke.h | 3 +++ arch/powerpc/include/asm/kvm_ppc.h | 5 +++++ arch/powerpc/kvm/book3s_64_mmu_hv.c | 26 ++++++++++---------------- arch/powerpc/kvm/powerpc.c | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 55 insertions(+), 16 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/include/asm/kvm_book3s.h b/arch/powerpc/include/asm/kvm_book3s.h index aa795ccef294..046041ff847f 100644 --- a/arch/powerpc/include/asm/kvm_book3s.h +++ b/arch/powerpc/include/asm/kvm_book3s.h @@ -452,4 +452,7 @@ static inline bool kvmppc_critical_section(struct kvm_vcpu *vcpu) #define INS_DCBZ 0x7c0007ec +/* LPIDs we support with this build -- runtime limit may be lower */ +#define KVMPPC_NR_LPIDS (LPID_RSVD + 1) + #endif /* __ASM_KVM_BOOK3S_H__ */ diff --git a/arch/powerpc/include/asm/kvm_booke.h b/arch/powerpc/include/asm/kvm_booke.h index a90e09188777..b7cd3356a532 100644 --- a/arch/powerpc/include/asm/kvm_booke.h +++ b/arch/powerpc/include/asm/kvm_booke.h @@ -23,6 +23,9 @@ #include #include +/* LPIDs we support with this build -- runtime limit may be lower */ +#define KVMPPC_NR_LPIDS 64 + static inline void kvmppc_set_gpr(struct kvm_vcpu *vcpu, int num, ulong val) { vcpu->arch.gpr[num] = val; diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index 9d6dee0f7d48..731e920eda1e 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -204,4 +204,9 @@ int kvm_vcpu_ioctl_config_tlb(struct kvm_vcpu *vcpu, int kvm_vcpu_ioctl_dirty_tlb(struct kvm_vcpu *vcpu, struct kvm_dirty_tlb *cfg); +long kvmppc_alloc_lpid(void); +void kvmppc_claim_lpid(long lpid); +void kvmppc_free_lpid(long lpid); +void kvmppc_init_lpid(unsigned long nr_lpids); + #endif /* __POWERPC_KVM_PPC_H__ */ diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c index ddc485a529f2..d031ce1d83f5 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c @@ -36,13 +36,11 @@ /* POWER7 has 10-bit LPIDs, PPC970 has 6-bit LPIDs */ #define MAX_LPID_970 63 -#define NR_LPIDS (LPID_RSVD + 1) -unsigned long lpid_inuse[BITS_TO_LONGS(NR_LPIDS)]; long kvmppc_alloc_hpt(struct kvm *kvm) { unsigned long hpt; - unsigned long lpid; + long lpid; struct revmap_entry *rev; struct kvmppc_linear_info *li; @@ -72,14 +70,9 @@ long kvmppc_alloc_hpt(struct kvm *kvm) } kvm->arch.revmap = rev; - /* Allocate the guest's logical partition ID */ - do { - lpid = find_first_zero_bit(lpid_inuse, NR_LPIDS); - if (lpid >= NR_LPIDS) { - pr_err("kvm_alloc_hpt: No LPIDs free\n"); - goto out_freeboth; - } - } while (test_and_set_bit(lpid, lpid_inuse)); + lpid = kvmppc_alloc_lpid(); + if (lpid < 0) + goto out_freeboth; kvm->arch.sdr1 = __pa(hpt) | (HPT_ORDER - 18); kvm->arch.lpid = lpid; @@ -96,7 +89,7 @@ long kvmppc_alloc_hpt(struct kvm *kvm) void kvmppc_free_hpt(struct kvm *kvm) { - clear_bit(kvm->arch.lpid, lpid_inuse); + kvmppc_free_lpid(kvm->arch.lpid); vfree(kvm->arch.revmap); if (kvm->arch.hpt_li) kvm_release_hpt(kvm->arch.hpt_li); @@ -171,8 +164,7 @@ int kvmppc_mmu_hv_init(void) if (!cpu_has_feature(CPU_FTR_HVMODE)) return -EINVAL; - memset(lpid_inuse, 0, sizeof(lpid_inuse)); - + /* POWER7 has 10-bit LPIDs, PPC970 and e500mc have 6-bit LPIDs */ if (cpu_has_feature(CPU_FTR_ARCH_206)) { host_lpid = mfspr(SPRN_LPID); /* POWER7 */ rsvd_lpid = LPID_RSVD; @@ -181,9 +173,11 @@ int kvmppc_mmu_hv_init(void) rsvd_lpid = MAX_LPID_970; } - set_bit(host_lpid, lpid_inuse); + kvmppc_init_lpid(rsvd_lpid + 1); + + kvmppc_claim_lpid(host_lpid); /* rsvd_lpid is reserved for use in partition switching */ - set_bit(rsvd_lpid, lpid_inuse); + kvmppc_claim_lpid(rsvd_lpid); return 0; } diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index b5e9046462fd..cd53e08403b3 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -799,6 +799,40 @@ out: return r; } +static unsigned long lpid_inuse[BITS_TO_LONGS(KVMPPC_NR_LPIDS)]; +static unsigned long nr_lpids; + +long kvmppc_alloc_lpid(void) +{ + long lpid; + + do { + lpid = find_first_zero_bit(lpid_inuse, KVMPPC_NR_LPIDS); + if (lpid >= nr_lpids) { + pr_err("%s: No LPIDs free\n", __func__); + return -ENOMEM; + } + } while (test_and_set_bit(lpid, lpid_inuse)); + + return lpid; +} + +void kvmppc_claim_lpid(long lpid) +{ + set_bit(lpid, lpid_inuse); +} + +void kvmppc_free_lpid(long lpid) +{ + clear_bit(lpid, lpid_inuse); +} + +void kvmppc_init_lpid(unsigned long nr_lpids_param) +{ + nr_lpids = min_t(unsigned long, KVMPPC_NR_LPIDS, nr_lpids_param); + memset(lpid_inuse, 0, sizeof(lpid_inuse)); +} + int kvm_arch_init(void *opaque) { return 0; -- cgit v1.2.3 From 94fa9d9927627a948cef3eff7ebd228dcab5a316 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:22 +0000 Subject: KVM: PPC: booke: add booke-level vcpu load/put This gives us a place to put load/put actions that correspond to code that is booke-specific but not specific to a particular core. Signed-off-by: Scott Wood Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/44x.c | 3 +++ arch/powerpc/kvm/booke.c | 8 ++++++++ arch/powerpc/kvm/booke.h | 3 +++ arch/powerpc/kvm/e500.c | 3 +++ 4 files changed, 17 insertions(+) (limited to 'arch') diff --git a/arch/powerpc/kvm/44x.c b/arch/powerpc/kvm/44x.c index 7b612a76c701..879a1a70acd0 100644 --- a/arch/powerpc/kvm/44x.c +++ b/arch/powerpc/kvm/44x.c @@ -29,15 +29,18 @@ #include #include "44x_tlb.h" +#include "booke.h" void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { + kvmppc_booke_vcpu_load(vcpu, cpu); kvmppc_44x_tlb_load(vcpu); } void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu) { kvmppc_44x_tlb_put(vcpu); + kvmppc_booke_vcpu_put(vcpu); } int kvmppc_core_check_processor_compat(void) diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index ee9e1ee9c858..a2456c7a1d87 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -968,6 +968,14 @@ void kvmppc_decrementer_func(unsigned long data) kvmppc_set_tsr_bits(vcpu, TSR_DIS); } +void kvmppc_booke_vcpu_load(struct kvm_vcpu *vcpu, int cpu) +{ +} + +void kvmppc_booke_vcpu_put(struct kvm_vcpu *vcpu) +{ +} + int __init kvmppc_booke_init(void) { unsigned long ivor[16]; diff --git a/arch/powerpc/kvm/booke.h b/arch/powerpc/kvm/booke.h index 2fe202705a3f..05d1d99428ce 100644 --- a/arch/powerpc/kvm/booke.h +++ b/arch/powerpc/kvm/booke.h @@ -71,4 +71,7 @@ void kvmppc_save_guest_spe(struct kvm_vcpu *vcpu); /* high-level function, manages flags, host state */ void kvmppc_vcpu_disable_spe(struct kvm_vcpu *vcpu); +void kvmppc_booke_vcpu_load(struct kvm_vcpu *vcpu, int cpu); +void kvmppc_booke_vcpu_put(struct kvm_vcpu *vcpu); + #endif /* __KVM_BOOKE_H__ */ diff --git a/arch/powerpc/kvm/e500.c b/arch/powerpc/kvm/e500.c index ddcd896fa2ff..2d5fe04d5eec 100644 --- a/arch/powerpc/kvm/e500.c +++ b/arch/powerpc/kvm/e500.c @@ -36,6 +36,7 @@ void kvmppc_core_load_guest_debugstate(struct kvm_vcpu *vcpu) void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { + kvmppc_booke_vcpu_load(vcpu, cpu); kvmppc_e500_tlb_load(vcpu, cpu); } @@ -47,6 +48,8 @@ void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu) if (vcpu->arch.shadow_msr & MSR_SPE) kvmppc_vcpu_disable_spe(vcpu); #endif + + kvmppc_booke_vcpu_put(vcpu); } int kvmppc_core_check_processor_compat(void) -- cgit v1.2.3 From fafd68327858bf30c846d38c7ea144f0827f552e Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:26 +0000 Subject: KVM: PPC: booke: Move vm core init/destroy out of booke.c e500mc will want to do lpid allocation/deallocation here. Signed-off-by: Scott Wood Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/44x.c | 9 +++++++++ arch/powerpc/kvm/booke.c | 9 --------- arch/powerpc/kvm/e500.c | 9 +++++++++ 3 files changed, 18 insertions(+), 9 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/44x.c b/arch/powerpc/kvm/44x.c index 879a1a70acd0..50e7dbc7356c 100644 --- a/arch/powerpc/kvm/44x.c +++ b/arch/powerpc/kvm/44x.c @@ -163,6 +163,15 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu) kmem_cache_free(kvm_vcpu_cache, vcpu_44x); } +int kvmppc_core_init_vm(struct kvm *kvm) +{ + return 0; +} + +void kvmppc_core_destroy_vm(struct kvm *kvm) +{ +} + static int __init kvmppc_44x_init(void) { int r; diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index a2456c7a1d87..2ee9bae38328 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -932,15 +932,6 @@ void kvmppc_core_commit_memory_region(struct kvm *kvm, { } -int kvmppc_core_init_vm(struct kvm *kvm) -{ - return 0; -} - -void kvmppc_core_destroy_vm(struct kvm *kvm) -{ -} - void kvmppc_set_tcr(struct kvm_vcpu *vcpu, u32 new_tcr) { vcpu->arch.tcr = new_tcr; diff --git a/arch/powerpc/kvm/e500.c b/arch/powerpc/kvm/e500.c index 2d5fe04d5eec..ac6c9ae14a63 100644 --- a/arch/powerpc/kvm/e500.c +++ b/arch/powerpc/kvm/e500.c @@ -226,6 +226,15 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu) kmem_cache_free(kvm_vcpu_cache, vcpu_e500); } +int kvmppc_core_init_vm(struct kvm *kvm) +{ + return 0; +} + +void kvmppc_core_destroy_vm(struct kvm *kvm) +{ +} + static int __init kvmppc_e500_init(void) { int r, i; -- cgit v1.2.3 From 29a5a6f9102aed97a06aa984cc294e0e603b3a79 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:29 +0000 Subject: KVM: PPC: e500: rename e500_tlb.h to e500.h This is in preparation for merging in the contents of arch/powerpc/include/asm/kvm_e500.h. Signed-off-by: Scott Wood Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/e500.c | 2 +- arch/powerpc/kvm/e500.h | 174 ++++++++++++++++++++++++++++++++++++++++ arch/powerpc/kvm/e500_emulate.c | 2 +- arch/powerpc/kvm/e500_tlb.c | 2 +- arch/powerpc/kvm/e500_tlb.h | 174 ---------------------------------------- 5 files changed, 177 insertions(+), 177 deletions(-) create mode 100644 arch/powerpc/kvm/e500.h delete mode 100644 arch/powerpc/kvm/e500_tlb.h (limited to 'arch') diff --git a/arch/powerpc/kvm/e500.c b/arch/powerpc/kvm/e500.c index ac6c9ae14a63..5c450ba003b8 100644 --- a/arch/powerpc/kvm/e500.c +++ b/arch/powerpc/kvm/e500.c @@ -24,7 +24,7 @@ #include #include "booke.h" -#include "e500_tlb.h" +#include "e500.h" void kvmppc_core_load_host_debugstate(struct kvm_vcpu *vcpu) { diff --git a/arch/powerpc/kvm/e500.h b/arch/powerpc/kvm/e500.h new file mode 100644 index 000000000000..02ecde261bc1 --- /dev/null +++ b/arch/powerpc/kvm/e500.h @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Yu Liu, yu.liu@freescale.com + * + * Description: + * This file is based on arch/powerpc/kvm/44x_tlb.h, + * by Hollis Blanchard . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + */ + +#ifndef KVM_E500_H +#define KVM_E500_H + +#include +#include +#include +#include + +/* This geometry is the legacy default -- can be overridden by userspace */ +#define KVM_E500_TLB0_WAY_SIZE 128 +#define KVM_E500_TLB0_WAY_NUM 2 + +#define KVM_E500_TLB0_SIZE (KVM_E500_TLB0_WAY_SIZE * KVM_E500_TLB0_WAY_NUM) +#define KVM_E500_TLB1_SIZE 16 + +#define index_of(tlbsel, esel) (((tlbsel) << 16) | ((esel) & 0xFFFF)) +#define tlbsel_of(index) ((index) >> 16) +#define esel_of(index) ((index) & 0xFFFF) + +#define E500_TLB_USER_PERM_MASK (MAS3_UX|MAS3_UR|MAS3_UW) +#define E500_TLB_SUPER_PERM_MASK (MAS3_SX|MAS3_SR|MAS3_SW) +#define MAS2_ATTRIB_MASK \ + (MAS2_X0 | MAS2_X1) +#define MAS3_ATTRIB_MASK \ + (MAS3_U0 | MAS3_U1 | MAS3_U2 | MAS3_U3 \ + | E500_TLB_USER_PERM_MASK | E500_TLB_SUPER_PERM_MASK) + +extern void kvmppc_dump_tlbs(struct kvm_vcpu *); +extern int kvmppc_e500_emul_mt_mmucsr0(struct kvmppc_vcpu_e500 *, ulong); +extern int kvmppc_e500_emul_tlbwe(struct kvm_vcpu *); +extern int kvmppc_e500_emul_tlbre(struct kvm_vcpu *); +extern int kvmppc_e500_emul_tlbivax(struct kvm_vcpu *, int, int); +extern int kvmppc_e500_emul_tlbsx(struct kvm_vcpu *, int); +extern int kvmppc_e500_tlb_search(struct kvm_vcpu *, gva_t, unsigned int, int); +extern void kvmppc_e500_tlb_put(struct kvm_vcpu *); +extern void kvmppc_e500_tlb_load(struct kvm_vcpu *, int); +extern int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *); +extern void kvmppc_e500_tlb_uninit(struct kvmppc_vcpu_e500 *); +extern void kvmppc_e500_tlb_setup(struct kvmppc_vcpu_e500 *); +extern void kvmppc_e500_recalc_shadow_pid(struct kvmppc_vcpu_e500 *); + +/* TLB helper functions */ +static inline unsigned int +get_tlb_size(const struct kvm_book3e_206_tlb_entry *tlbe) +{ + return (tlbe->mas1 >> 7) & 0x1f; +} + +static inline gva_t get_tlb_eaddr(const struct kvm_book3e_206_tlb_entry *tlbe) +{ + return tlbe->mas2 & 0xfffff000; +} + +static inline u64 get_tlb_bytes(const struct kvm_book3e_206_tlb_entry *tlbe) +{ + unsigned int pgsize = get_tlb_size(tlbe); + return 1ULL << 10 << pgsize; +} + +static inline gva_t get_tlb_end(const struct kvm_book3e_206_tlb_entry *tlbe) +{ + u64 bytes = get_tlb_bytes(tlbe); + return get_tlb_eaddr(tlbe) + bytes - 1; +} + +static inline u64 get_tlb_raddr(const struct kvm_book3e_206_tlb_entry *tlbe) +{ + return tlbe->mas7_3 & ~0xfffULL; +} + +static inline unsigned int +get_tlb_tid(const struct kvm_book3e_206_tlb_entry *tlbe) +{ + return (tlbe->mas1 >> 16) & 0xff; +} + +static inline unsigned int +get_tlb_ts(const struct kvm_book3e_206_tlb_entry *tlbe) +{ + return (tlbe->mas1 >> 12) & 0x1; +} + +static inline unsigned int +get_tlb_v(const struct kvm_book3e_206_tlb_entry *tlbe) +{ + return (tlbe->mas1 >> 31) & 0x1; +} + +static inline unsigned int +get_tlb_iprot(const struct kvm_book3e_206_tlb_entry *tlbe) +{ + return (tlbe->mas1 >> 30) & 0x1; +} + +static inline unsigned int get_cur_pid(struct kvm_vcpu *vcpu) +{ + return vcpu->arch.pid & 0xff; +} + +static inline unsigned int get_cur_as(struct kvm_vcpu *vcpu) +{ + return !!(vcpu->arch.shared->msr & (MSR_IS | MSR_DS)); +} + +static inline unsigned int get_cur_pr(struct kvm_vcpu *vcpu) +{ + return !!(vcpu->arch.shared->msr & MSR_PR); +} + +static inline unsigned int get_cur_spid(const struct kvm_vcpu *vcpu) +{ + return (vcpu->arch.shared->mas6 >> 16) & 0xff; +} + +static inline unsigned int get_cur_sas(const struct kvm_vcpu *vcpu) +{ + return vcpu->arch.shared->mas6 & 0x1; +} + +static inline unsigned int get_tlb_tlbsel(const struct kvm_vcpu *vcpu) +{ + /* + * Manual says that tlbsel has 2 bits wide. + * Since we only have two TLBs, only lower bit is used. + */ + return (vcpu->arch.shared->mas0 >> 28) & 0x1; +} + +static inline unsigned int get_tlb_nv_bit(const struct kvm_vcpu *vcpu) +{ + return vcpu->arch.shared->mas0 & 0xfff; +} + +static inline unsigned int get_tlb_esel_bit(const struct kvm_vcpu *vcpu) +{ + return (vcpu->arch.shared->mas0 >> 16) & 0xfff; +} + +static inline int tlbe_is_host_safe(const struct kvm_vcpu *vcpu, + const struct kvm_book3e_206_tlb_entry *tlbe) +{ + gpa_t gpa; + + if (!get_tlb_v(tlbe)) + return 0; + + /* Does it match current guest AS? */ + /* XXX what about IS != DS? */ + if (get_tlb_ts(tlbe) != !!(vcpu->arch.shared->msr & MSR_IS)) + return 0; + + gpa = get_tlb_raddr(tlbe); + if (!gfn_to_memslot(vcpu->kvm, gpa >> PAGE_SHIFT)) + /* Mapping is not for RAM. */ + return 0; + + return 1; +} + +#endif /* KVM_E500_H */ diff --git a/arch/powerpc/kvm/e500_emulate.c b/arch/powerpc/kvm/e500_emulate.c index 6d0b2bd54fb0..2a1a22822066 100644 --- a/arch/powerpc/kvm/e500_emulate.c +++ b/arch/powerpc/kvm/e500_emulate.c @@ -17,7 +17,7 @@ #include #include "booke.h" -#include "e500_tlb.h" +#include "e500.h" #define XOP_TLBIVAX 786 #define XOP_TLBSX 914 diff --git a/arch/powerpc/kvm/e500_tlb.c b/arch/powerpc/kvm/e500_tlb.c index 6e53e4164de1..1d623a085dcc 100644 --- a/arch/powerpc/kvm/e500_tlb.c +++ b/arch/powerpc/kvm/e500_tlb.c @@ -29,7 +29,7 @@ #include #include "../mm/mmu_decl.h" -#include "e500_tlb.h" +#include "e500.h" #include "trace.h" #include "timing.h" diff --git a/arch/powerpc/kvm/e500_tlb.h b/arch/powerpc/kvm/e500_tlb.h deleted file mode 100644 index 5c6d2d7bf058..000000000000 --- a/arch/powerpc/kvm/e500_tlb.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Yu Liu, yu.liu@freescale.com - * - * Description: - * This file is based on arch/powerpc/kvm/44x_tlb.h, - * by Hollis Blanchard . - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - */ - -#ifndef __KVM_E500_TLB_H__ -#define __KVM_E500_TLB_H__ - -#include -#include -#include -#include - -/* This geometry is the legacy default -- can be overridden by userspace */ -#define KVM_E500_TLB0_WAY_SIZE 128 -#define KVM_E500_TLB0_WAY_NUM 2 - -#define KVM_E500_TLB0_SIZE (KVM_E500_TLB0_WAY_SIZE * KVM_E500_TLB0_WAY_NUM) -#define KVM_E500_TLB1_SIZE 16 - -#define index_of(tlbsel, esel) (((tlbsel) << 16) | ((esel) & 0xFFFF)) -#define tlbsel_of(index) ((index) >> 16) -#define esel_of(index) ((index) & 0xFFFF) - -#define E500_TLB_USER_PERM_MASK (MAS3_UX|MAS3_UR|MAS3_UW) -#define E500_TLB_SUPER_PERM_MASK (MAS3_SX|MAS3_SR|MAS3_SW) -#define MAS2_ATTRIB_MASK \ - (MAS2_X0 | MAS2_X1) -#define MAS3_ATTRIB_MASK \ - (MAS3_U0 | MAS3_U1 | MAS3_U2 | MAS3_U3 \ - | E500_TLB_USER_PERM_MASK | E500_TLB_SUPER_PERM_MASK) - -extern void kvmppc_dump_tlbs(struct kvm_vcpu *); -extern int kvmppc_e500_emul_mt_mmucsr0(struct kvmppc_vcpu_e500 *, ulong); -extern int kvmppc_e500_emul_tlbwe(struct kvm_vcpu *); -extern int kvmppc_e500_emul_tlbre(struct kvm_vcpu *); -extern int kvmppc_e500_emul_tlbivax(struct kvm_vcpu *, int, int); -extern int kvmppc_e500_emul_tlbsx(struct kvm_vcpu *, int); -extern int kvmppc_e500_tlb_search(struct kvm_vcpu *, gva_t, unsigned int, int); -extern void kvmppc_e500_tlb_put(struct kvm_vcpu *); -extern void kvmppc_e500_tlb_load(struct kvm_vcpu *, int); -extern int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *); -extern void kvmppc_e500_tlb_uninit(struct kvmppc_vcpu_e500 *); -extern void kvmppc_e500_tlb_setup(struct kvmppc_vcpu_e500 *); -extern void kvmppc_e500_recalc_shadow_pid(struct kvmppc_vcpu_e500 *); - -/* TLB helper functions */ -static inline unsigned int -get_tlb_size(const struct kvm_book3e_206_tlb_entry *tlbe) -{ - return (tlbe->mas1 >> 7) & 0x1f; -} - -static inline gva_t get_tlb_eaddr(const struct kvm_book3e_206_tlb_entry *tlbe) -{ - return tlbe->mas2 & 0xfffff000; -} - -static inline u64 get_tlb_bytes(const struct kvm_book3e_206_tlb_entry *tlbe) -{ - unsigned int pgsize = get_tlb_size(tlbe); - return 1ULL << 10 << pgsize; -} - -static inline gva_t get_tlb_end(const struct kvm_book3e_206_tlb_entry *tlbe) -{ - u64 bytes = get_tlb_bytes(tlbe); - return get_tlb_eaddr(tlbe) + bytes - 1; -} - -static inline u64 get_tlb_raddr(const struct kvm_book3e_206_tlb_entry *tlbe) -{ - return tlbe->mas7_3 & ~0xfffULL; -} - -static inline unsigned int -get_tlb_tid(const struct kvm_book3e_206_tlb_entry *tlbe) -{ - return (tlbe->mas1 >> 16) & 0xff; -} - -static inline unsigned int -get_tlb_ts(const struct kvm_book3e_206_tlb_entry *tlbe) -{ - return (tlbe->mas1 >> 12) & 0x1; -} - -static inline unsigned int -get_tlb_v(const struct kvm_book3e_206_tlb_entry *tlbe) -{ - return (tlbe->mas1 >> 31) & 0x1; -} - -static inline unsigned int -get_tlb_iprot(const struct kvm_book3e_206_tlb_entry *tlbe) -{ - return (tlbe->mas1 >> 30) & 0x1; -} - -static inline unsigned int get_cur_pid(struct kvm_vcpu *vcpu) -{ - return vcpu->arch.pid & 0xff; -} - -static inline unsigned int get_cur_as(struct kvm_vcpu *vcpu) -{ - return !!(vcpu->arch.shared->msr & (MSR_IS | MSR_DS)); -} - -static inline unsigned int get_cur_pr(struct kvm_vcpu *vcpu) -{ - return !!(vcpu->arch.shared->msr & MSR_PR); -} - -static inline unsigned int get_cur_spid(const struct kvm_vcpu *vcpu) -{ - return (vcpu->arch.shared->mas6 >> 16) & 0xff; -} - -static inline unsigned int get_cur_sas(const struct kvm_vcpu *vcpu) -{ - return vcpu->arch.shared->mas6 & 0x1; -} - -static inline unsigned int get_tlb_tlbsel(const struct kvm_vcpu *vcpu) -{ - /* - * Manual says that tlbsel has 2 bits wide. - * Since we only have two TLBs, only lower bit is used. - */ - return (vcpu->arch.shared->mas0 >> 28) & 0x1; -} - -static inline unsigned int get_tlb_nv_bit(const struct kvm_vcpu *vcpu) -{ - return vcpu->arch.shared->mas0 & 0xfff; -} - -static inline unsigned int get_tlb_esel_bit(const struct kvm_vcpu *vcpu) -{ - return (vcpu->arch.shared->mas0 >> 16) & 0xfff; -} - -static inline int tlbe_is_host_safe(const struct kvm_vcpu *vcpu, - const struct kvm_book3e_206_tlb_entry *tlbe) -{ - gpa_t gpa; - - if (!get_tlb_v(tlbe)) - return 0; - - /* Does it match current guest AS? */ - /* XXX what about IS != DS? */ - if (get_tlb_ts(tlbe) != !!(vcpu->arch.shared->msr & MSR_IS)) - return 0; - - gpa = get_tlb_raddr(tlbe); - if (!gfn_to_memslot(vcpu->kvm, gpa >> PAGE_SHIFT)) - /* Mapping is not for RAM. */ - return 0; - - return 1; -} - -#endif /* __KVM_E500_TLB_H__ */ -- cgit v1.2.3 From fc6cf99509eb8e5f16e0f81db0c71f5301193005 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:31 +0000 Subject: KVM: PPC: e500: merge into arch/powerpc/kvm/e500.h Keeping two separate headers for e500-specific things was a pain, and wasn't even organized along any logical boundary. There was TLB stuff in despite the existence of arch/powerpc/kvm/e500_tlb.h, and nothing in needed to be referenced from outside arch/powerpc/kvm. Signed-off-by: Scott Wood [agraf: fix bisectability] Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/kvm_e500.h | 96 ------------------------------------- arch/powerpc/kvm/e500.c | 1 - arch/powerpc/kvm/e500.h | 82 +++++++++++++++++++++++++++++-- arch/powerpc/kvm/e500_emulate.c | 1 - arch/powerpc/kvm/e500_tlb.c | 1 - 5 files changed, 78 insertions(+), 103 deletions(-) delete mode 100644 arch/powerpc/include/asm/kvm_e500.h (limited to 'arch') diff --git a/arch/powerpc/include/asm/kvm_e500.h b/arch/powerpc/include/asm/kvm_e500.h deleted file mode 100644 index 8cd50a514271..000000000000 --- a/arch/powerpc/include/asm/kvm_e500.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. All rights reserved. - * - * Author: Yu Liu, - * - * Description: - * This file is derived from arch/powerpc/include/asm/kvm_44x.h, - * by Hollis Blanchard . - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - */ - -#ifndef __ASM_KVM_E500_H__ -#define __ASM_KVM_E500_H__ - -#include - -#define BOOKE_INTERRUPT_SIZE 36 - -#define E500_PID_NUM 3 -#define E500_TLB_NUM 2 - -#define E500_TLB_VALID 1 -#define E500_TLB_DIRTY 2 - -struct tlbe_ref { - pfn_t pfn; - unsigned int flags; /* E500_TLB_* */ -}; - -struct tlbe_priv { - struct tlbe_ref ref; /* TLB0 only -- TLB1 uses tlb_refs */ -}; - -struct vcpu_id_table; - -struct kvmppc_e500_tlb_params { - int entries, ways, sets; -}; - -struct kvmppc_vcpu_e500 { - /* Unmodified copy of the guest's TLB -- shared with host userspace. */ - struct kvm_book3e_206_tlb_entry *gtlb_arch; - - /* Starting entry number in gtlb_arch[] */ - int gtlb_offset[E500_TLB_NUM]; - - /* KVM internal information associated with each guest TLB entry */ - struct tlbe_priv *gtlb_priv[E500_TLB_NUM]; - - struct kvmppc_e500_tlb_params gtlb_params[E500_TLB_NUM]; - - unsigned int gtlb_nv[E500_TLB_NUM]; - - /* - * information associated with each host TLB entry -- - * TLB1 only for now. If/when guest TLB1 entries can be - * mapped with host TLB0, this will be used for that too. - * - * We don't want to use this for guest TLB0 because then we'd - * have the overhead of doing the translation again even if - * the entry is still in the guest TLB (e.g. we swapped out - * and back, and our host TLB entries got evicted). - */ - struct tlbe_ref *tlb_refs[E500_TLB_NUM]; - unsigned int host_tlb1_nv; - - u32 host_pid[E500_PID_NUM]; - u32 pid[E500_PID_NUM]; - u32 svr; - - /* vcpu id table */ - struct vcpu_id_table *idt; - - u32 l1csr0; - u32 l1csr1; - u32 hid0; - u32 hid1; - u32 tlb0cfg; - u32 tlb1cfg; - u64 mcar; - - struct page **shared_tlb_pages; - int num_shared_tlb_pages; - - struct kvm_vcpu vcpu; -}; - -static inline struct kvmppc_vcpu_e500 *to_e500(struct kvm_vcpu *vcpu) -{ - return container_of(vcpu, struct kvmppc_vcpu_e500, vcpu); -} - -#endif /* __ASM_KVM_E500_H__ */ diff --git a/arch/powerpc/kvm/e500.c b/arch/powerpc/kvm/e500.c index 5c450ba003b8..76b35d8f099f 100644 --- a/arch/powerpc/kvm/e500.c +++ b/arch/powerpc/kvm/e500.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include "booke.h" diff --git a/arch/powerpc/kvm/e500.h b/arch/powerpc/kvm/e500.h index 02ecde261bc1..51d13bd40153 100644 --- a/arch/powerpc/kvm/e500.h +++ b/arch/powerpc/kvm/e500.h @@ -1,11 +1,12 @@ /* * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. All rights reserved. * - * Author: Yu Liu, yu.liu@freescale.com + * Author: Yu Liu * * Description: - * This file is based on arch/powerpc/kvm/44x_tlb.h, - * by Hollis Blanchard . + * This file is based on arch/powerpc/kvm/44x_tlb.h and + * arch/powerpc/include/asm/kvm_44x.h by Hollis Blanchard , + * Copyright IBM Corp. 2007-2008 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as @@ -18,7 +19,80 @@ #include #include #include -#include + +#define E500_PID_NUM 3 +#define E500_TLB_NUM 2 + +#define E500_TLB_VALID 1 +#define E500_TLB_DIRTY 2 + +struct tlbe_ref { + pfn_t pfn; + unsigned int flags; /* E500_TLB_* */ +}; + +struct tlbe_priv { + struct tlbe_ref ref; /* TLB0 only -- TLB1 uses tlb_refs */ +}; + +struct vcpu_id_table; + +struct kvmppc_e500_tlb_params { + int entries, ways, sets; +}; + +struct kvmppc_vcpu_e500 { + /* Unmodified copy of the guest's TLB -- shared with host userspace. */ + struct kvm_book3e_206_tlb_entry *gtlb_arch; + + /* Starting entry number in gtlb_arch[] */ + int gtlb_offset[E500_TLB_NUM]; + + /* KVM internal information associated with each guest TLB entry */ + struct tlbe_priv *gtlb_priv[E500_TLB_NUM]; + + struct kvmppc_e500_tlb_params gtlb_params[E500_TLB_NUM]; + + unsigned int gtlb_nv[E500_TLB_NUM]; + + /* + * information associated with each host TLB entry -- + * TLB1 only for now. If/when guest TLB1 entries can be + * mapped with host TLB0, this will be used for that too. + * + * We don't want to use this for guest TLB0 because then we'd + * have the overhead of doing the translation again even if + * the entry is still in the guest TLB (e.g. we swapped out + * and back, and our host TLB entries got evicted). + */ + struct tlbe_ref *tlb_refs[E500_TLB_NUM]; + unsigned int host_tlb1_nv; + + u32 host_pid[E500_PID_NUM]; + u32 pid[E500_PID_NUM]; + u32 svr; + + /* vcpu id table */ + struct vcpu_id_table *idt; + + u32 l1csr0; + u32 l1csr1; + u32 hid0; + u32 hid1; + u32 tlb0cfg; + u32 tlb1cfg; + u64 mcar; + + struct page **shared_tlb_pages; + int num_shared_tlb_pages; + + struct kvm_vcpu vcpu; +}; + +static inline struct kvmppc_vcpu_e500 *to_e500(struct kvm_vcpu *vcpu) +{ + return container_of(vcpu, struct kvmppc_vcpu_e500, vcpu); +} /* This geometry is the legacy default -- can be overridden by userspace */ #define KVM_E500_TLB0_WAY_SIZE 128 diff --git a/arch/powerpc/kvm/e500_emulate.c b/arch/powerpc/kvm/e500_emulate.c index 2a1a22822066..7e2d592bf562 100644 --- a/arch/powerpc/kvm/e500_emulate.c +++ b/arch/powerpc/kvm/e500_emulate.c @@ -14,7 +14,6 @@ #include #include -#include #include "booke.h" #include "e500.h" diff --git a/arch/powerpc/kvm/e500_tlb.c b/arch/powerpc/kvm/e500_tlb.c index 1d623a085dcc..7d4a918a0ab0 100644 --- a/arch/powerpc/kvm/e500_tlb.c +++ b/arch/powerpc/kvm/e500_tlb.c @@ -26,7 +26,6 @@ #include #include #include -#include #include "../mm/mmu_decl.h" #include "e500.h" -- cgit v1.2.3 From 52e1718c6fd1a1f54c676c2107dc931e93865fe8 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:32 +0000 Subject: KVM: PPC: e500: clean up arch/powerpc/kvm/e500.h Move vcpu to the beginning of vcpu_e500 to give it appropriate prominence, especially if more fields end up getting added to the end of vcpu_e500 (and vcpu ends up in the middle). Remove gratuitous "extern" and add parameter names to prototypes. Signed-off-by: Scott Wood [agraf: fix bisectability] Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/e500.h | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/e500.h b/arch/powerpc/kvm/e500.h index 51d13bd40153..a48af005c223 100644 --- a/arch/powerpc/kvm/e500.h +++ b/arch/powerpc/kvm/e500.h @@ -42,6 +42,8 @@ struct kvmppc_e500_tlb_params { }; struct kvmppc_vcpu_e500 { + struct kvm_vcpu vcpu; + /* Unmodified copy of the guest's TLB -- shared with host userspace. */ struct kvm_book3e_206_tlb_entry *gtlb_arch; @@ -85,8 +87,6 @@ struct kvmppc_vcpu_e500 { struct page **shared_tlb_pages; int num_shared_tlb_pages; - - struct kvm_vcpu vcpu; }; static inline struct kvmppc_vcpu_e500 *to_e500(struct kvm_vcpu *vcpu) @@ -113,19 +113,22 @@ static inline struct kvmppc_vcpu_e500 *to_e500(struct kvm_vcpu *vcpu) (MAS3_U0 | MAS3_U1 | MAS3_U2 | MAS3_U3 \ | E500_TLB_USER_PERM_MASK | E500_TLB_SUPER_PERM_MASK) -extern void kvmppc_dump_tlbs(struct kvm_vcpu *); -extern int kvmppc_e500_emul_mt_mmucsr0(struct kvmppc_vcpu_e500 *, ulong); -extern int kvmppc_e500_emul_tlbwe(struct kvm_vcpu *); -extern int kvmppc_e500_emul_tlbre(struct kvm_vcpu *); -extern int kvmppc_e500_emul_tlbivax(struct kvm_vcpu *, int, int); -extern int kvmppc_e500_emul_tlbsx(struct kvm_vcpu *, int); -extern int kvmppc_e500_tlb_search(struct kvm_vcpu *, gva_t, unsigned int, int); extern void kvmppc_e500_tlb_put(struct kvm_vcpu *); extern void kvmppc_e500_tlb_load(struct kvm_vcpu *, int); -extern int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *); -extern void kvmppc_e500_tlb_uninit(struct kvmppc_vcpu_e500 *); extern void kvmppc_e500_tlb_setup(struct kvmppc_vcpu_e500 *); extern void kvmppc_e500_recalc_shadow_pid(struct kvmppc_vcpu_e500 *); +int kvmppc_e500_emul_mt_mmucsr0(struct kvmppc_vcpu_e500 *vcpu_e500, + ulong value); +int kvmppc_e500_emul_tlbwe(struct kvm_vcpu *vcpu); +int kvmppc_e500_emul_tlbre(struct kvm_vcpu *vcpu); +int kvmppc_e500_emul_tlbivax(struct kvm_vcpu *vcpu, int ra, int rb); +int kvmppc_e500_emul_tlbsx(struct kvm_vcpu *vcpu, int rb); +int kvmppc_e500_tlb_search(struct kvm_vcpu *, gva_t, unsigned int, int); +int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500); +void kvmppc_e500_tlb_uninit(struct kvmppc_vcpu_e500 *vcpu_e500); + +void kvmppc_get_sregs_e500_tlb(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs); +int kvmppc_set_sregs_e500_tlb(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs); /* TLB helper functions */ static inline unsigned int -- cgit v1.2.3 From 8fdd21a26876ea6c486c38bfa75fdd18ba299351 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:34 +0000 Subject: KVM: PPC: e500: refactor core-specific TLB code The PID handling is e500v1/v2-specific, and is moved to e500.c. The MMU sregs code and kvmppc_core_vcpu_translate will be shared with e500mc, and is moved from e500.c to e500_tlb.c. Partially based on patches from Liu Yu . Signed-off-by: Scott Wood [agraf: fix bisectability] Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/kvm_host.h | 2 + arch/powerpc/kvm/e500.c | 357 ++++++++++++++++++++++++---- arch/powerpc/kvm/e500.h | 62 ++++- arch/powerpc/kvm/e500_emulate.c | 6 +- arch/powerpc/kvm/e500_tlb.c | 460 +++++++++--------------------------- 5 files changed, 473 insertions(+), 414 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 20ab5b2dbd0f..5b81cbc43a42 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -426,6 +426,8 @@ struct kvm_vcpu_arch { ulong fault_esr; ulong queued_dear; ulong queued_esr; + u32 tlbcfg[4]; + u32 mmucfg; #endif gpa_t paddr_accessed; diff --git a/arch/powerpc/kvm/e500.c b/arch/powerpc/kvm/e500.c index 76b35d8f099f..b479ed77c515 100644 --- a/arch/powerpc/kvm/e500.c +++ b/arch/powerpc/kvm/e500.c @@ -22,9 +22,281 @@ #include #include +#include "../mm/mmu_decl.h" #include "booke.h" #include "e500.h" +struct id { + unsigned long val; + struct id **pentry; +}; + +#define NUM_TIDS 256 + +/* + * This table provide mappings from: + * (guestAS,guestTID,guestPR) --> ID of physical cpu + * guestAS [0..1] + * guestTID [0..255] + * guestPR [0..1] + * ID [1..255] + * Each vcpu keeps one vcpu_id_table. + */ +struct vcpu_id_table { + struct id id[2][NUM_TIDS][2]; +}; + +/* + * This table provide reversed mappings of vcpu_id_table: + * ID --> address of vcpu_id_table item. + * Each physical core has one pcpu_id_table. + */ +struct pcpu_id_table { + struct id *entry[NUM_TIDS]; +}; + +static DEFINE_PER_CPU(struct pcpu_id_table, pcpu_sids); + +/* This variable keeps last used shadow ID on local core. + * The valid range of shadow ID is [1..255] */ +static DEFINE_PER_CPU(unsigned long, pcpu_last_used_sid); + +/* + * Allocate a free shadow id and setup a valid sid mapping in given entry. + * A mapping is only valid when vcpu_id_table and pcpu_id_table are match. + * + * The caller must have preemption disabled, and keep it that way until + * it has finished with the returned shadow id (either written into the + * TLB or arch.shadow_pid, or discarded). + */ +static inline int local_sid_setup_one(struct id *entry) +{ + unsigned long sid; + int ret = -1; + + sid = ++(__get_cpu_var(pcpu_last_used_sid)); + if (sid < NUM_TIDS) { + __get_cpu_var(pcpu_sids).entry[sid] = entry; + entry->val = sid; + entry->pentry = &__get_cpu_var(pcpu_sids).entry[sid]; + ret = sid; + } + + /* + * If sid == NUM_TIDS, we've run out of sids. We return -1, and + * the caller will invalidate everything and start over. + * + * sid > NUM_TIDS indicates a race, which we disable preemption to + * avoid. + */ + WARN_ON(sid > NUM_TIDS); + + return ret; +} + +/* + * Check if given entry contain a valid shadow id mapping. + * An ID mapping is considered valid only if + * both vcpu and pcpu know this mapping. + * + * The caller must have preemption disabled, and keep it that way until + * it has finished with the returned shadow id (either written into the + * TLB or arch.shadow_pid, or discarded). + */ +static inline int local_sid_lookup(struct id *entry) +{ + if (entry && entry->val != 0 && + __get_cpu_var(pcpu_sids).entry[entry->val] == entry && + entry->pentry == &__get_cpu_var(pcpu_sids).entry[entry->val]) + return entry->val; + return -1; +} + +/* Invalidate all id mappings on local core -- call with preempt disabled */ +static inline void local_sid_destroy_all(void) +{ + __get_cpu_var(pcpu_last_used_sid) = 0; + memset(&__get_cpu_var(pcpu_sids), 0, sizeof(__get_cpu_var(pcpu_sids))); +} + +static void *kvmppc_e500_id_table_alloc(struct kvmppc_vcpu_e500 *vcpu_e500) +{ + vcpu_e500->idt = kzalloc(sizeof(struct vcpu_id_table), GFP_KERNEL); + return vcpu_e500->idt; +} + +static void kvmppc_e500_id_table_free(struct kvmppc_vcpu_e500 *vcpu_e500) +{ + kfree(vcpu_e500->idt); + vcpu_e500->idt = NULL; +} + +/* Map guest pid to shadow. + * We use PID to keep shadow of current guest non-zero PID, + * and use PID1 to keep shadow of guest zero PID. + * So that guest tlbe with TID=0 can be accessed at any time */ +static void kvmppc_e500_recalc_shadow_pid(struct kvmppc_vcpu_e500 *vcpu_e500) +{ + preempt_disable(); + vcpu_e500->vcpu.arch.shadow_pid = kvmppc_e500_get_sid(vcpu_e500, + get_cur_as(&vcpu_e500->vcpu), + get_cur_pid(&vcpu_e500->vcpu), + get_cur_pr(&vcpu_e500->vcpu), 1); + vcpu_e500->vcpu.arch.shadow_pid1 = kvmppc_e500_get_sid(vcpu_e500, + get_cur_as(&vcpu_e500->vcpu), 0, + get_cur_pr(&vcpu_e500->vcpu), 1); + preempt_enable(); +} + +/* Invalidate all mappings on vcpu */ +static void kvmppc_e500_id_table_reset_all(struct kvmppc_vcpu_e500 *vcpu_e500) +{ + memset(vcpu_e500->idt, 0, sizeof(struct vcpu_id_table)); + + /* Update shadow pid when mappings are changed */ + kvmppc_e500_recalc_shadow_pid(vcpu_e500); +} + +/* Invalidate one ID mapping on vcpu */ +static inline void kvmppc_e500_id_table_reset_one( + struct kvmppc_vcpu_e500 *vcpu_e500, + int as, int pid, int pr) +{ + struct vcpu_id_table *idt = vcpu_e500->idt; + + BUG_ON(as >= 2); + BUG_ON(pid >= NUM_TIDS); + BUG_ON(pr >= 2); + + idt->id[as][pid][pr].val = 0; + idt->id[as][pid][pr].pentry = NULL; + + /* Update shadow pid when mappings are changed */ + kvmppc_e500_recalc_shadow_pid(vcpu_e500); +} + +/* + * Map guest (vcpu,AS,ID,PR) to physical core shadow id. + * This function first lookup if a valid mapping exists, + * if not, then creates a new one. + * + * The caller must have preemption disabled, and keep it that way until + * it has finished with the returned shadow id (either written into the + * TLB or arch.shadow_pid, or discarded). + */ +unsigned int kvmppc_e500_get_sid(struct kvmppc_vcpu_e500 *vcpu_e500, + unsigned int as, unsigned int gid, + unsigned int pr, int avoid_recursion) +{ + struct vcpu_id_table *idt = vcpu_e500->idt; + int sid; + + BUG_ON(as >= 2); + BUG_ON(gid >= NUM_TIDS); + BUG_ON(pr >= 2); + + sid = local_sid_lookup(&idt->id[as][gid][pr]); + + while (sid <= 0) { + /* No mapping yet */ + sid = local_sid_setup_one(&idt->id[as][gid][pr]); + if (sid <= 0) { + _tlbil_all(); + local_sid_destroy_all(); + } + + /* Update shadow pid when mappings are changed */ + if (!avoid_recursion) + kvmppc_e500_recalc_shadow_pid(vcpu_e500); + } + + return sid; +} + +unsigned int kvmppc_e500_get_tlb_stid(struct kvm_vcpu *vcpu, + struct kvm_book3e_206_tlb_entry *gtlbe) +{ + return kvmppc_e500_get_sid(to_e500(vcpu), get_tlb_ts(gtlbe), + get_tlb_tid(gtlbe), get_cur_pr(vcpu), 0); +} + +void kvmppc_set_pid(struct kvm_vcpu *vcpu, u32 pid) +{ + struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); + + if (vcpu->arch.pid != pid) { + vcpu_e500->pid[0] = vcpu->arch.pid = pid; + kvmppc_e500_recalc_shadow_pid(vcpu_e500); + } +} + +/* gtlbe must not be mapped by more than one host tlbe */ +void kvmppc_e500_tlbil_one(struct kvmppc_vcpu_e500 *vcpu_e500, + struct kvm_book3e_206_tlb_entry *gtlbe) +{ + struct vcpu_id_table *idt = vcpu_e500->idt; + unsigned int pr, tid, ts, pid; + u32 val, eaddr; + unsigned long flags; + + ts = get_tlb_ts(gtlbe); + tid = get_tlb_tid(gtlbe); + + preempt_disable(); + + /* One guest ID may be mapped to two shadow IDs */ + for (pr = 0; pr < 2; pr++) { + /* + * The shadow PID can have a valid mapping on at most one + * host CPU. In the common case, it will be valid on this + * CPU, in which case we do a local invalidation of the + * specific address. + * + * If the shadow PID is not valid on the current host CPU, + * we invalidate the entire shadow PID. + */ + pid = local_sid_lookup(&idt->id[ts][tid][pr]); + if (pid <= 0) { + kvmppc_e500_id_table_reset_one(vcpu_e500, ts, tid, pr); + continue; + } + + /* + * The guest is invalidating a 4K entry which is in a PID + * that has a valid shadow mapping on this host CPU. We + * search host TLB to invalidate it's shadow TLB entry, + * similar to __tlbil_va except that we need to look in AS1. + */ + val = (pid << MAS6_SPID_SHIFT) | MAS6_SAS; + eaddr = get_tlb_eaddr(gtlbe); + + local_irq_save(flags); + + mtspr(SPRN_MAS6, val); + asm volatile("tlbsx 0, %[eaddr]" : : [eaddr] "r" (eaddr)); + val = mfspr(SPRN_MAS1); + if (val & MAS1_VALID) { + mtspr(SPRN_MAS1, val & ~MAS1_VALID); + asm volatile("tlbwe"); + } + + local_irq_restore(flags); + } + + preempt_enable(); +} + +void kvmppc_e500_tlbil_all(struct kvmppc_vcpu_e500 *vcpu_e500) +{ + kvmppc_e500_id_table_reset_all(vcpu_e500); +} + +void kvmppc_mmu_msr_notify(struct kvm_vcpu *vcpu, u32 old_msr) +{ + /* Recalc shadow pid since MSR changes */ + kvmppc_e500_recalc_shadow_pid(to_e500(vcpu)); +} + void kvmppc_core_load_host_debugstate(struct kvm_vcpu *vcpu) { } @@ -36,13 +308,13 @@ void kvmppc_core_load_guest_debugstate(struct kvm_vcpu *vcpu) void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { kvmppc_booke_vcpu_load(vcpu, cpu); - kvmppc_e500_tlb_load(vcpu, cpu); + + /* Shadow PID may be expired on local core */ + kvmppc_e500_recalc_shadow_pid(to_e500(vcpu)); } void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu) { - kvmppc_e500_tlb_put(vcpu); - #ifdef CONFIG_SPE if (vcpu->arch.shadow_msr & MSR_SPE) kvmppc_vcpu_disable_spe(vcpu); @@ -63,6 +335,23 @@ int kvmppc_core_check_processor_compat(void) return r; } +static void kvmppc_e500_tlb_setup(struct kvmppc_vcpu_e500 *vcpu_e500) +{ + struct kvm_book3e_206_tlb_entry *tlbe; + + /* Insert large initial mapping for guest. */ + tlbe = get_entry(vcpu_e500, 1, 0); + tlbe->mas1 = MAS1_VALID | MAS1_TSIZE(BOOK3E_PAGESZ_256M); + tlbe->mas2 = 0; + tlbe->mas7_3 = E500_TLB_SUPER_PERM_MASK; + + /* 4K map for serial output. Used by kernel wrapper. */ + tlbe = get_entry(vcpu_e500, 1, 1); + tlbe->mas1 = MAS1_VALID | MAS1_TSIZE(BOOK3E_PAGESZ_4K); + tlbe->mas2 = (0xe0004500 & 0xFFFFF000) | MAS2_I | MAS2_G; + tlbe->mas7_3 = (0xe0004500 & 0xFFFFF000) | E500_TLB_SUPER_PERM_MASK; +} + int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu) { struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); @@ -78,32 +367,6 @@ int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu) return 0; } -/* 'linear_address' is actually an encoding of AS|PID|EADDR . */ -int kvmppc_core_vcpu_translate(struct kvm_vcpu *vcpu, - struct kvm_translation *tr) -{ - int index; - gva_t eaddr; - u8 pid; - u8 as; - - eaddr = tr->linear_address; - pid = (tr->linear_address >> 32) & 0xff; - as = (tr->linear_address >> 40) & 0x1; - - index = kvmppc_e500_tlb_search(vcpu, eaddr, pid, as); - if (index < 0) { - tr->valid = 0; - return 0; - } - - tr->physical_address = kvmppc_mmu_xlate(vcpu, index, eaddr); - /* XXX what does "writeable" and "usermode" even mean? */ - tr->valid = 1; - - return 0; -} - void kvmppc_core_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) { struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); @@ -117,19 +380,6 @@ void kvmppc_core_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) sregs->u.e.impl.fsl.hid0 = vcpu_e500->hid0; sregs->u.e.impl.fsl.mcar = vcpu_e500->mcar; - sregs->u.e.mas0 = vcpu->arch.shared->mas0; - sregs->u.e.mas1 = vcpu->arch.shared->mas1; - sregs->u.e.mas2 = vcpu->arch.shared->mas2; - sregs->u.e.mas7_3 = vcpu->arch.shared->mas7_3; - sregs->u.e.mas4 = vcpu->arch.shared->mas4; - sregs->u.e.mas6 = vcpu->arch.shared->mas6; - - sregs->u.e.mmucfg = mfspr(SPRN_MMUCFG); - sregs->u.e.tlbcfg[0] = vcpu_e500->tlb0cfg; - sregs->u.e.tlbcfg[1] = vcpu_e500->tlb1cfg; - sregs->u.e.tlbcfg[2] = 0; - sregs->u.e.tlbcfg[3] = 0; - sregs->u.e.ivor_high[0] = vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_UNAVAIL]; sregs->u.e.ivor_high[1] = vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_FP_DATA]; sregs->u.e.ivor_high[2] = vcpu->arch.ivor[BOOKE_IRQPRIO_SPE_FP_ROUND]; @@ -137,11 +387,13 @@ void kvmppc_core_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) vcpu->arch.ivor[BOOKE_IRQPRIO_PERFORMANCE_MONITOR]; kvmppc_get_sregs_ivor(vcpu, sregs); + kvmppc_get_sregs_e500_tlb(vcpu, sregs); } int kvmppc_core_set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) { struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); + int ret; if (sregs->u.e.impl_id == KVM_SREGS_E_IMPL_FSL) { vcpu_e500->svr = sregs->u.e.impl.fsl.svr; @@ -149,14 +401,9 @@ int kvmppc_core_set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) vcpu_e500->mcar = sregs->u.e.impl.fsl.mcar; } - if (sregs->u.e.features & KVM_SREGS_E_ARCH206_MMU) { - vcpu->arch.shared->mas0 = sregs->u.e.mas0; - vcpu->arch.shared->mas1 = sregs->u.e.mas1; - vcpu->arch.shared->mas2 = sregs->u.e.mas2; - vcpu->arch.shared->mas7_3 = sregs->u.e.mas7_3; - vcpu->arch.shared->mas4 = sregs->u.e.mas4; - vcpu->arch.shared->mas6 = sregs->u.e.mas6; - } + ret = kvmppc_set_sregs_e500_tlb(vcpu, sregs); + if (ret < 0) + return ret; if (!(sregs->u.e.features & KVM_SREGS_E_IVOR)) return 0; @@ -195,9 +442,12 @@ struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id) if (err) goto free_vcpu; + if (kvmppc_e500_id_table_alloc(vcpu_e500) == NULL) + goto uninit_vcpu; + err = kvmppc_e500_tlb_init(vcpu_e500); if (err) - goto uninit_vcpu; + goto uninit_id; vcpu->arch.shared = (void*)__get_free_page(GFP_KERNEL|__GFP_ZERO); if (!vcpu->arch.shared) @@ -207,6 +457,8 @@ struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id) uninit_tlb: kvmppc_e500_tlb_uninit(vcpu_e500); +uninit_id: + kvmppc_e500_id_table_free(vcpu_e500); uninit_vcpu: kvm_vcpu_uninit(vcpu); free_vcpu: @@ -220,8 +472,9 @@ void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu) struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); free_page((unsigned long)vcpu->arch.shared); - kvm_vcpu_uninit(vcpu); kvmppc_e500_tlb_uninit(vcpu_e500); + kvmppc_e500_id_table_free(vcpu_e500); + kvm_vcpu_uninit(vcpu); kmem_cache_free(kvm_vcpu_cache, vcpu_e500); } diff --git a/arch/powerpc/kvm/e500.h b/arch/powerpc/kvm/e500.h index a48af005c223..34cef08f1361 100644 --- a/arch/powerpc/kvm/e500.h +++ b/arch/powerpc/kvm/e500.h @@ -35,7 +35,9 @@ struct tlbe_priv { struct tlbe_ref ref; /* TLB0 only -- TLB1 uses tlb_refs */ }; +#ifdef CONFIG_KVM_E500 struct vcpu_id_table; +#endif struct kvmppc_e500_tlb_params { int entries, ways, sets; @@ -70,23 +72,22 @@ struct kvmppc_vcpu_e500 { struct tlbe_ref *tlb_refs[E500_TLB_NUM]; unsigned int host_tlb1_nv; - u32 host_pid[E500_PID_NUM]; - u32 pid[E500_PID_NUM]; u32 svr; - - /* vcpu id table */ - struct vcpu_id_table *idt; - u32 l1csr0; u32 l1csr1; u32 hid0; u32 hid1; - u32 tlb0cfg; - u32 tlb1cfg; u64 mcar; struct page **shared_tlb_pages; int num_shared_tlb_pages; + +#ifdef CONFIG_KVM_E500 + u32 pid[E500_PID_NUM]; + + /* vcpu id table */ + struct vcpu_id_table *idt; +#endif }; static inline struct kvmppc_vcpu_e500 *to_e500(struct kvm_vcpu *vcpu) @@ -113,23 +114,25 @@ static inline struct kvmppc_vcpu_e500 *to_e500(struct kvm_vcpu *vcpu) (MAS3_U0 | MAS3_U1 | MAS3_U2 | MAS3_U3 \ | E500_TLB_USER_PERM_MASK | E500_TLB_SUPER_PERM_MASK) -extern void kvmppc_e500_tlb_put(struct kvm_vcpu *); -extern void kvmppc_e500_tlb_load(struct kvm_vcpu *, int); -extern void kvmppc_e500_tlb_setup(struct kvmppc_vcpu_e500 *); -extern void kvmppc_e500_recalc_shadow_pid(struct kvmppc_vcpu_e500 *); int kvmppc_e500_emul_mt_mmucsr0(struct kvmppc_vcpu_e500 *vcpu_e500, ulong value); int kvmppc_e500_emul_tlbwe(struct kvm_vcpu *vcpu); int kvmppc_e500_emul_tlbre(struct kvm_vcpu *vcpu); int kvmppc_e500_emul_tlbivax(struct kvm_vcpu *vcpu, int ra, int rb); int kvmppc_e500_emul_tlbsx(struct kvm_vcpu *vcpu, int rb); -int kvmppc_e500_tlb_search(struct kvm_vcpu *, gva_t, unsigned int, int); int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500); void kvmppc_e500_tlb_uninit(struct kvmppc_vcpu_e500 *vcpu_e500); void kvmppc_get_sregs_e500_tlb(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs); int kvmppc_set_sregs_e500_tlb(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs); + +#ifdef CONFIG_KVM_E500 +unsigned int kvmppc_e500_get_sid(struct kvmppc_vcpu_e500 *vcpu_e500, + unsigned int as, unsigned int gid, + unsigned int pr, int avoid_recursion); +#endif + /* TLB helper functions */ static inline unsigned int get_tlb_size(const struct kvm_book3e_206_tlb_entry *tlbe) @@ -183,6 +186,12 @@ get_tlb_iprot(const struct kvm_book3e_206_tlb_entry *tlbe) return (tlbe->mas1 >> 30) & 0x1; } +static inline unsigned int +get_tlb_tsize(const struct kvm_book3e_206_tlb_entry *tlbe) +{ + return (tlbe->mas1 & MAS1_TSIZE_MASK) >> MAS1_TSIZE_SHIFT; +} + static inline unsigned int get_cur_pid(struct kvm_vcpu *vcpu) { return vcpu->arch.pid & 0xff; @@ -248,4 +257,31 @@ static inline int tlbe_is_host_safe(const struct kvm_vcpu *vcpu, return 1; } +static inline struct kvm_book3e_206_tlb_entry *get_entry( + struct kvmppc_vcpu_e500 *vcpu_e500, int tlbsel, int entry) +{ + int offset = vcpu_e500->gtlb_offset[tlbsel]; + return &vcpu_e500->gtlb_arch[offset + entry]; +} + +void kvmppc_e500_tlbil_one(struct kvmppc_vcpu_e500 *vcpu_e500, + struct kvm_book3e_206_tlb_entry *gtlbe); +void kvmppc_e500_tlbil_all(struct kvmppc_vcpu_e500 *vcpu_e500); + +#ifdef CONFIG_KVM_E500 +unsigned int kvmppc_e500_get_tlb_stid(struct kvm_vcpu *vcpu, + struct kvm_book3e_206_tlb_entry *gtlbe); + +static inline unsigned int get_tlbmiss_tid(struct kvm_vcpu *vcpu) +{ + struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); + unsigned int tidseld = (vcpu->arch.shared->mas4 >> 16) & 0xf; + + return vcpu_e500->pid[tidseld]; +} + +/* Force TS=1 for all guest mappings. */ +#define get_tlb_sts(gtlbe) (MAS1_TS) +#endif /* CONFIG_KVM_E500 */ + #endif /* KVM_E500_H */ diff --git a/arch/powerpc/kvm/e500_emulate.c b/arch/powerpc/kvm/e500_emulate.c index 7e2d592bf562..c80794d097d3 100644 --- a/arch/powerpc/kvm/e500_emulate.c +++ b/arch/powerpc/kvm/e500_emulate.c @@ -174,9 +174,9 @@ int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt) kvmppc_set_gpr(vcpu, rt, val); break; case SPRN_TLB0CFG: - kvmppc_set_gpr(vcpu, rt, vcpu_e500->tlb0cfg); break; + kvmppc_set_gpr(vcpu, rt, vcpu->arch.tlbcfg[0]); break; case SPRN_TLB1CFG: - kvmppc_set_gpr(vcpu, rt, vcpu_e500->tlb1cfg); break; + kvmppc_set_gpr(vcpu, rt, vcpu->arch.tlbcfg[1]); break; case SPRN_L1CSR0: kvmppc_set_gpr(vcpu, rt, vcpu_e500->l1csr0); break; case SPRN_L1CSR1: @@ -192,7 +192,7 @@ int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt) kvmppc_set_gpr(vcpu, rt, 0); break; case SPRN_MMUCFG: - kvmppc_set_gpr(vcpu, rt, mfspr(SPRN_MMUCFG)); break; + kvmppc_set_gpr(vcpu, rt, vcpu->arch.mmucfg); break; /* extra exceptions */ case SPRN_IVOR32: diff --git a/arch/powerpc/kvm/e500_tlb.c b/arch/powerpc/kvm/e500_tlb.c index 7d4a918a0ab0..9925fc6c9cfb 100644 --- a/arch/powerpc/kvm/e500_tlb.c +++ b/arch/powerpc/kvm/e500_tlb.c @@ -27,208 +27,14 @@ #include #include -#include "../mm/mmu_decl.h" #include "e500.h" #include "trace.h" #include "timing.h" #define to_htlb1_esel(esel) (host_tlb_params[1].entries - (esel) - 1) -struct id { - unsigned long val; - struct id **pentry; -}; - -#define NUM_TIDS 256 - -/* - * This table provide mappings from: - * (guestAS,guestTID,guestPR) --> ID of physical cpu - * guestAS [0..1] - * guestTID [0..255] - * guestPR [0..1] - * ID [1..255] - * Each vcpu keeps one vcpu_id_table. - */ -struct vcpu_id_table { - struct id id[2][NUM_TIDS][2]; -}; - -/* - * This table provide reversed mappings of vcpu_id_table: - * ID --> address of vcpu_id_table item. - * Each physical core has one pcpu_id_table. - */ -struct pcpu_id_table { - struct id *entry[NUM_TIDS]; -}; - -static DEFINE_PER_CPU(struct pcpu_id_table, pcpu_sids); - -/* This variable keeps last used shadow ID on local core. - * The valid range of shadow ID is [1..255] */ -static DEFINE_PER_CPU(unsigned long, pcpu_last_used_sid); - static struct kvmppc_e500_tlb_params host_tlb_params[E500_TLB_NUM]; -static struct kvm_book3e_206_tlb_entry *get_entry( - struct kvmppc_vcpu_e500 *vcpu_e500, int tlbsel, int entry) -{ - int offset = vcpu_e500->gtlb_offset[tlbsel]; - return &vcpu_e500->gtlb_arch[offset + entry]; -} - -/* - * Allocate a free shadow id and setup a valid sid mapping in given entry. - * A mapping is only valid when vcpu_id_table and pcpu_id_table are match. - * - * The caller must have preemption disabled, and keep it that way until - * it has finished with the returned shadow id (either written into the - * TLB or arch.shadow_pid, or discarded). - */ -static inline int local_sid_setup_one(struct id *entry) -{ - unsigned long sid; - int ret = -1; - - sid = ++(__get_cpu_var(pcpu_last_used_sid)); - if (sid < NUM_TIDS) { - __get_cpu_var(pcpu_sids).entry[sid] = entry; - entry->val = sid; - entry->pentry = &__get_cpu_var(pcpu_sids).entry[sid]; - ret = sid; - } - - /* - * If sid == NUM_TIDS, we've run out of sids. We return -1, and - * the caller will invalidate everything and start over. - * - * sid > NUM_TIDS indicates a race, which we disable preemption to - * avoid. - */ - WARN_ON(sid > NUM_TIDS); - - return ret; -} - -/* - * Check if given entry contain a valid shadow id mapping. - * An ID mapping is considered valid only if - * both vcpu and pcpu know this mapping. - * - * The caller must have preemption disabled, and keep it that way until - * it has finished with the returned shadow id (either written into the - * TLB or arch.shadow_pid, or discarded). - */ -static inline int local_sid_lookup(struct id *entry) -{ - if (entry && entry->val != 0 && - __get_cpu_var(pcpu_sids).entry[entry->val] == entry && - entry->pentry == &__get_cpu_var(pcpu_sids).entry[entry->val]) - return entry->val; - return -1; -} - -/* Invalidate all id mappings on local core -- call with preempt disabled */ -static inline void local_sid_destroy_all(void) -{ - __get_cpu_var(pcpu_last_used_sid) = 0; - memset(&__get_cpu_var(pcpu_sids), 0, sizeof(__get_cpu_var(pcpu_sids))); -} - -static void *kvmppc_e500_id_table_alloc(struct kvmppc_vcpu_e500 *vcpu_e500) -{ - vcpu_e500->idt = kzalloc(sizeof(struct vcpu_id_table), GFP_KERNEL); - return vcpu_e500->idt; -} - -static void kvmppc_e500_id_table_free(struct kvmppc_vcpu_e500 *vcpu_e500) -{ - kfree(vcpu_e500->idt); -} - -/* Invalidate all mappings on vcpu */ -static void kvmppc_e500_id_table_reset_all(struct kvmppc_vcpu_e500 *vcpu_e500) -{ - memset(vcpu_e500->idt, 0, sizeof(struct vcpu_id_table)); - - /* Update shadow pid when mappings are changed */ - kvmppc_e500_recalc_shadow_pid(vcpu_e500); -} - -/* Invalidate one ID mapping on vcpu */ -static inline void kvmppc_e500_id_table_reset_one( - struct kvmppc_vcpu_e500 *vcpu_e500, - int as, int pid, int pr) -{ - struct vcpu_id_table *idt = vcpu_e500->idt; - - BUG_ON(as >= 2); - BUG_ON(pid >= NUM_TIDS); - BUG_ON(pr >= 2); - - idt->id[as][pid][pr].val = 0; - idt->id[as][pid][pr].pentry = NULL; - - /* Update shadow pid when mappings are changed */ - kvmppc_e500_recalc_shadow_pid(vcpu_e500); -} - -/* - * Map guest (vcpu,AS,ID,PR) to physical core shadow id. - * This function first lookup if a valid mapping exists, - * if not, then creates a new one. - * - * The caller must have preemption disabled, and keep it that way until - * it has finished with the returned shadow id (either written into the - * TLB or arch.shadow_pid, or discarded). - */ -static unsigned int kvmppc_e500_get_sid(struct kvmppc_vcpu_e500 *vcpu_e500, - unsigned int as, unsigned int gid, - unsigned int pr, int avoid_recursion) -{ - struct vcpu_id_table *idt = vcpu_e500->idt; - int sid; - - BUG_ON(as >= 2); - BUG_ON(gid >= NUM_TIDS); - BUG_ON(pr >= 2); - - sid = local_sid_lookup(&idt->id[as][gid][pr]); - - while (sid <= 0) { - /* No mapping yet */ - sid = local_sid_setup_one(&idt->id[as][gid][pr]); - if (sid <= 0) { - _tlbil_all(); - local_sid_destroy_all(); - } - - /* Update shadow pid when mappings are changed */ - if (!avoid_recursion) - kvmppc_e500_recalc_shadow_pid(vcpu_e500); - } - - return sid; -} - -/* Map guest pid to shadow. - * We use PID to keep shadow of current guest non-zero PID, - * and use PID1 to keep shadow of guest zero PID. - * So that guest tlbe with TID=0 can be accessed at any time */ -void kvmppc_e500_recalc_shadow_pid(struct kvmppc_vcpu_e500 *vcpu_e500) -{ - preempt_disable(); - vcpu_e500->vcpu.arch.shadow_pid = kvmppc_e500_get_sid(vcpu_e500, - get_cur_as(&vcpu_e500->vcpu), - get_cur_pid(&vcpu_e500->vcpu), - get_cur_pr(&vcpu_e500->vcpu), 1); - vcpu_e500->vcpu.arch.shadow_pid1 = kvmppc_e500_get_sid(vcpu_e500, - get_cur_as(&vcpu_e500->vcpu), 0, - get_cur_pr(&vcpu_e500->vcpu), 1); - preempt_enable(); -} - static inline unsigned int gtlb0_get_next_victim( struct kvmppc_vcpu_e500 *vcpu_e500) { @@ -336,6 +142,7 @@ static inline void write_host_tlbe(struct kvmppc_vcpu_e500 *vcpu_e500, } } +#ifdef CONFIG_KVM_E500 void kvmppc_map_magic(struct kvm_vcpu *vcpu) { struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); @@ -360,75 +167,21 @@ void kvmppc_map_magic(struct kvm_vcpu *vcpu) __write_host_tlbe(&magic, MAS0_TLBSEL(1) | MAS0_ESEL(tlbcam_index)); preempt_enable(); } - -void kvmppc_e500_tlb_load(struct kvm_vcpu *vcpu, int cpu) -{ - struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); - - /* Shadow PID may be expired on local core */ - kvmppc_e500_recalc_shadow_pid(vcpu_e500); -} - -void kvmppc_e500_tlb_put(struct kvm_vcpu *vcpu) -{ -} +#endif static void inval_gtlbe_on_host(struct kvmppc_vcpu_e500 *vcpu_e500, int tlbsel, int esel) { struct kvm_book3e_206_tlb_entry *gtlbe = get_entry(vcpu_e500, tlbsel, esel); - struct vcpu_id_table *idt = vcpu_e500->idt; - unsigned int pr, tid, ts, pid; - u32 val, eaddr; - unsigned long flags; - - ts = get_tlb_ts(gtlbe); - tid = get_tlb_tid(gtlbe); - - preempt_disable(); - - /* One guest ID may be mapped to two shadow IDs */ - for (pr = 0; pr < 2; pr++) { - /* - * The shadow PID can have a valid mapping on at most one - * host CPU. In the common case, it will be valid on this - * CPU, in which case (for TLB0) we do a local invalidation - * of the specific address. - * - * If the shadow PID is not valid on the current host CPU, or - * if we're invalidating a TLB1 entry, we invalidate the - * entire shadow PID. - */ - if (tlbsel == 1 || - (pid = local_sid_lookup(&idt->id[ts][tid][pr])) <= 0) { - kvmppc_e500_id_table_reset_one(vcpu_e500, ts, tid, pr); - continue; - } - - /* - * The guest is invalidating a TLB0 entry which is in a PID - * that has a valid shadow mapping on this host CPU. We - * search host TLB0 to invalidate it's shadow TLB entry, - * similar to __tlbil_va except that we need to look in AS1. - */ - val = (pid << MAS6_SPID_SHIFT) | MAS6_SAS; - eaddr = get_tlb_eaddr(gtlbe); - - local_irq_save(flags); - - mtspr(SPRN_MAS6, val); - asm volatile("tlbsx 0, %[eaddr]" : : [eaddr] "r" (eaddr)); - val = mfspr(SPRN_MAS1); - if (val & MAS1_VALID) { - mtspr(SPRN_MAS1, val & ~MAS1_VALID); - asm volatile("tlbwe"); - } - local_irq_restore(flags); + if (tlbsel == 1) { + kvmppc_e500_tlbil_all(vcpu_e500); + return; } - preempt_enable(); + /* Guest tlbe is backed by at most one host tlbe per shadow pid. */ + kvmppc_e500_tlbil_one(vcpu_e500, gtlbe); } static int tlb0_set_base(gva_t addr, int sets, int ways) @@ -546,7 +299,7 @@ static void clear_tlb_refs(struct kvmppc_vcpu_e500 *vcpu_e500) int stlbsel = 1; int i; - kvmppc_e500_id_table_reset_all(vcpu_e500); + kvmppc_e500_tlbil_all(vcpu_e500); for (i = 0; i < host_tlb_params[stlbsel].entries; i++) { struct tlbe_ref *ref = @@ -561,19 +314,18 @@ static inline void kvmppc_e500_deliver_tlb_miss(struct kvm_vcpu *vcpu, unsigned int eaddr, int as) { struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); - unsigned int victim, pidsel, tsized; + unsigned int victim, tsized; int tlbsel; /* since we only have two TLBs, only lower bit is used. */ tlbsel = (vcpu->arch.shared->mas4 >> 28) & 0x1; victim = (tlbsel == 0) ? gtlb0_get_next_victim(vcpu_e500) : 0; - pidsel = (vcpu->arch.shared->mas4 >> 16) & 0xf; tsized = (vcpu->arch.shared->mas4 >> 7) & 0x1f; vcpu->arch.shared->mas0 = MAS0_TLBSEL(tlbsel) | MAS0_ESEL(victim) | MAS0_NV(vcpu_e500->gtlb_nv[tlbsel]); vcpu->arch.shared->mas1 = MAS1_VALID | (as ? MAS1_TS : 0) - | MAS1_TID(vcpu_e500->pid[pidsel]) + | MAS1_TID(get_tlbmiss_tid(vcpu)) | MAS1_TSIZE(tsized); vcpu->arch.shared->mas2 = (eaddr & MAS2_EPN) | (vcpu->arch.shared->mas4 & MAS2_ATTRIB_MASK); @@ -585,23 +337,22 @@ static inline void kvmppc_e500_deliver_tlb_miss(struct kvm_vcpu *vcpu, /* TID must be supplied by the caller */ static inline void kvmppc_e500_setup_stlbe( - struct kvmppc_vcpu_e500 *vcpu_e500, + struct kvm_vcpu *vcpu, struct kvm_book3e_206_tlb_entry *gtlbe, int tsize, struct tlbe_ref *ref, u64 gvaddr, struct kvm_book3e_206_tlb_entry *stlbe) { pfn_t pfn = ref->pfn; + u32 pr = vcpu->arch.shared->msr & MSR_PR; BUG_ON(!(ref->flags & E500_TLB_VALID)); - /* Force TS=1 IPROT=0 for all guest mappings. */ - stlbe->mas1 = MAS1_TSIZE(tsize) | MAS1_TS | MAS1_VALID; - stlbe->mas2 = (gvaddr & MAS2_EPN) - | e500_shadow_mas2_attrib(gtlbe->mas2, - vcpu_e500->vcpu.arch.shared->msr & MSR_PR); - stlbe->mas7_3 = ((u64)pfn << PAGE_SHIFT) - | e500_shadow_mas3_attrib(gtlbe->mas7_3, - vcpu_e500->vcpu.arch.shared->msr & MSR_PR); + /* Force IPROT=0 for all guest mappings. */ + stlbe->mas1 = MAS1_TSIZE(tsize) | get_tlb_sts(gtlbe) | MAS1_VALID; + stlbe->mas2 = (gvaddr & MAS2_EPN) | + e500_shadow_mas2_attrib(gtlbe->mas2, pr); + stlbe->mas7_3 = ((u64)pfn << PAGE_SHIFT) | + e500_shadow_mas3_attrib(gtlbe->mas7_3, pr); } static inline void kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500, @@ -735,7 +486,8 @@ static inline void kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500, kvmppc_e500_ref_release(ref); kvmppc_e500_ref_setup(ref, gtlbe, pfn); - kvmppc_e500_setup_stlbe(vcpu_e500, gtlbe, tsize, ref, gvaddr, stlbe); + kvmppc_e500_setup_stlbe(&vcpu_e500->vcpu, gtlbe, tsize, + ref, gvaddr, stlbe); } /* XXX only map the one-one case, for now use TLB0 */ @@ -775,14 +527,6 @@ static int kvmppc_e500_tlb1_map(struct kvmppc_vcpu_e500 *vcpu_e500, return victim; } -void kvmppc_mmu_msr_notify(struct kvm_vcpu *vcpu, u32 old_msr) -{ - struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); - - /* Recalc shadow pid since MSR changes */ - kvmppc_e500_recalc_shadow_pid(vcpu_e500); -} - static inline int kvmppc_e500_gtlbe_invalidate( struct kvmppc_vcpu_e500 *vcpu_e500, int tlbsel, int esel) @@ -810,7 +554,7 @@ int kvmppc_e500_emul_mt_mmucsr0(struct kvmppc_vcpu_e500 *vcpu_e500, ulong value) kvmppc_e500_gtlbe_invalidate(vcpu_e500, 1, esel); /* Invalidate all vcpu id mappings */ - kvmppc_e500_id_table_reset_all(vcpu_e500); + kvmppc_e500_tlbil_all(vcpu_e500); return EMULATE_DONE; } @@ -843,7 +587,7 @@ int kvmppc_e500_emul_tlbivax(struct kvm_vcpu *vcpu, int ra, int rb) } /* Invalidate all vcpu id mappings */ - kvmppc_e500_id_table_reset_all(vcpu_e500); + kvmppc_e500_tlbil_all(vcpu_e500); return EMULATE_DONE; } @@ -928,9 +672,7 @@ static void write_stlbe(struct kvmppc_vcpu_e500 *vcpu_e500, int stid; preempt_disable(); - stid = kvmppc_e500_get_sid(vcpu_e500, get_tlb_ts(gtlbe), - get_tlb_tid(gtlbe), - get_cur_pr(&vcpu_e500->vcpu), 0); + stid = kvmppc_e500_get_tlb_stid(&vcpu_e500->vcpu, gtlbe); stlbe->mas1 |= MAS1_TID(stid); write_host_tlbe(vcpu_e500, stlbsel, sesel, stlbe); @@ -940,8 +682,8 @@ static void write_stlbe(struct kvmppc_vcpu_e500 *vcpu_e500, int kvmppc_e500_emul_tlbwe(struct kvm_vcpu *vcpu) { struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); - struct kvm_book3e_206_tlb_entry *gtlbe; - int tlbsel, esel; + struct kvm_book3e_206_tlb_entry *gtlbe, stlbe; + int tlbsel, esel, stlbsel, sesel; tlbsel = get_tlb_tlbsel(vcpu); esel = get_tlb_esel(vcpu, tlbsel); @@ -960,8 +702,6 @@ int kvmppc_e500_emul_tlbwe(struct kvm_vcpu *vcpu) /* Invalidate shadow mappings for the about-to-be-clobbered TLBE. */ if (tlbe_is_host_safe(vcpu, gtlbe)) { - struct kvm_book3e_206_tlb_entry stlbe; - int stlbsel, sesel; u64 eaddr; u64 raddr; @@ -988,7 +728,7 @@ int kvmppc_e500_emul_tlbwe(struct kvm_vcpu *vcpu) * are mapped on the fly. */ stlbsel = 1; sesel = kvmppc_e500_tlb1_map(vcpu_e500, eaddr, - raddr >> PAGE_SHIFT, gtlbe, &stlbe); + raddr >> PAGE_SHIFT, gtlbe, &stlbe); break; default: @@ -1002,6 +742,48 @@ int kvmppc_e500_emul_tlbwe(struct kvm_vcpu *vcpu) return EMULATE_DONE; } +static int kvmppc_e500_tlb_search(struct kvm_vcpu *vcpu, + gva_t eaddr, unsigned int pid, int as) +{ + struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); + int esel, tlbsel; + + for (tlbsel = 0; tlbsel < 2; tlbsel++) { + esel = kvmppc_e500_tlb_index(vcpu_e500, eaddr, tlbsel, pid, as); + if (esel >= 0) + return index_of(tlbsel, esel); + } + + return -1; +} + +/* 'linear_address' is actually an encoding of AS|PID|EADDR . */ +int kvmppc_core_vcpu_translate(struct kvm_vcpu *vcpu, + struct kvm_translation *tr) +{ + int index; + gva_t eaddr; + u8 pid; + u8 as; + + eaddr = tr->linear_address; + pid = (tr->linear_address >> 32) & 0xff; + as = (tr->linear_address >> 40) & 0x1; + + index = kvmppc_e500_tlb_search(vcpu, eaddr, pid, as); + if (index < 0) { + tr->valid = 0; + return 0; + } + + tr->physical_address = kvmppc_mmu_xlate(vcpu, index, eaddr); + /* XXX what does "writeable" and "usermode" even mean? */ + tr->valid = 1; + + return 0; +} + + int kvmppc_mmu_itlb_index(struct kvm_vcpu *vcpu, gva_t eaddr) { unsigned int as = !!(vcpu->arch.shared->msr & MSR_IS); @@ -1065,7 +847,7 @@ void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 eaddr, gpa_t gpaddr, sesel = 0; /* unused */ priv = &vcpu_e500->gtlb_priv[tlbsel][esel]; - kvmppc_e500_setup_stlbe(vcpu_e500, gtlbe, BOOK3E_PAGESZ_4K, + kvmppc_e500_setup_stlbe(vcpu, gtlbe, BOOK3E_PAGESZ_4K, &priv->ref, eaddr, &stlbe); break; @@ -1086,48 +868,6 @@ void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 eaddr, gpa_t gpaddr, write_stlbe(vcpu_e500, gtlbe, &stlbe, stlbsel, sesel); } -int kvmppc_e500_tlb_search(struct kvm_vcpu *vcpu, - gva_t eaddr, unsigned int pid, int as) -{ - struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); - int esel, tlbsel; - - for (tlbsel = 0; tlbsel < 2; tlbsel++) { - esel = kvmppc_e500_tlb_index(vcpu_e500, eaddr, tlbsel, pid, as); - if (esel >= 0) - return index_of(tlbsel, esel); - } - - return -1; -} - -void kvmppc_set_pid(struct kvm_vcpu *vcpu, u32 pid) -{ - struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); - - if (vcpu->arch.pid != pid) { - vcpu_e500->pid[0] = vcpu->arch.pid = pid; - kvmppc_e500_recalc_shadow_pid(vcpu_e500); - } -} - -void kvmppc_e500_tlb_setup(struct kvmppc_vcpu_e500 *vcpu_e500) -{ - struct kvm_book3e_206_tlb_entry *tlbe; - - /* Insert large initial mapping for guest. */ - tlbe = get_entry(vcpu_e500, 1, 0); - tlbe->mas1 = MAS1_VALID | MAS1_TSIZE(BOOK3E_PAGESZ_256M); - tlbe->mas2 = 0; - tlbe->mas7_3 = E500_TLB_SUPER_PERM_MASK; - - /* 4K map for serial output. Used by kernel wrapper. */ - tlbe = get_entry(vcpu_e500, 1, 1); - tlbe->mas1 = MAS1_VALID | MAS1_TSIZE(BOOK3E_PAGESZ_4K); - tlbe->mas2 = (0xe0004500 & 0xFFFFF000) | MAS2_I | MAS2_G; - tlbe->mas7_3 = (0xe0004500 & 0xFFFFF000) | E500_TLB_SUPER_PERM_MASK; -} - static void free_gtlb(struct kvmppc_vcpu_e500 *vcpu_e500) { int i; @@ -1154,6 +894,36 @@ static void free_gtlb(struct kvmppc_vcpu_e500 *vcpu_e500) vcpu_e500->gtlb_arch = NULL; } +void kvmppc_get_sregs_e500_tlb(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) +{ + sregs->u.e.mas0 = vcpu->arch.shared->mas0; + sregs->u.e.mas1 = vcpu->arch.shared->mas1; + sregs->u.e.mas2 = vcpu->arch.shared->mas2; + sregs->u.e.mas7_3 = vcpu->arch.shared->mas7_3; + sregs->u.e.mas4 = vcpu->arch.shared->mas4; + sregs->u.e.mas6 = vcpu->arch.shared->mas6; + + sregs->u.e.mmucfg = vcpu->arch.mmucfg; + sregs->u.e.tlbcfg[0] = vcpu->arch.tlbcfg[0]; + sregs->u.e.tlbcfg[1] = vcpu->arch.tlbcfg[1]; + sregs->u.e.tlbcfg[2] = 0; + sregs->u.e.tlbcfg[3] = 0; +} + +int kvmppc_set_sregs_e500_tlb(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) +{ + if (sregs->u.e.features & KVM_SREGS_E_ARCH206_MMU) { + vcpu->arch.shared->mas0 = sregs->u.e.mas0; + vcpu->arch.shared->mas1 = sregs->u.e.mas1; + vcpu->arch.shared->mas2 = sregs->u.e.mas2; + vcpu->arch.shared->mas7_3 = sregs->u.e.mas7_3; + vcpu->arch.shared->mas4 = sregs->u.e.mas4; + vcpu->arch.shared->mas6 = sregs->u.e.mas6; + } + + return 0; +} + int kvm_vcpu_ioctl_config_tlb(struct kvm_vcpu *vcpu, struct kvm_config_tlb *cfg) { @@ -1237,14 +1007,16 @@ int kvm_vcpu_ioctl_config_tlb(struct kvm_vcpu *vcpu, vcpu_e500->gtlb_offset[0] = 0; vcpu_e500->gtlb_offset[1] = params.tlb_sizes[0]; - vcpu_e500->tlb0cfg &= ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC); + vcpu->arch.mmucfg = mfspr(SPRN_MMUCFG) & ~MMUCFG_LPIDSIZE; + + vcpu->arch.tlbcfg[0] &= ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC); if (params.tlb_sizes[0] <= 2048) - vcpu_e500->tlb0cfg |= params.tlb_sizes[0]; - vcpu_e500->tlb0cfg |= params.tlb_ways[0] << TLBnCFG_ASSOC_SHIFT; + vcpu->arch.tlbcfg[0] |= params.tlb_sizes[0]; + vcpu->arch.tlbcfg[0] |= params.tlb_ways[0] << TLBnCFG_ASSOC_SHIFT; - vcpu_e500->tlb1cfg &= ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC); - vcpu_e500->tlb1cfg |= params.tlb_sizes[1]; - vcpu_e500->tlb1cfg |= params.tlb_ways[1] << TLBnCFG_ASSOC_SHIFT; + vcpu->arch.tlbcfg[1] &= ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC); + vcpu->arch.tlbcfg[1] |= params.tlb_sizes[1]; + vcpu->arch.tlbcfg[1] |= params.tlb_ways[1] << TLBnCFG_ASSOC_SHIFT; vcpu_e500->shared_tlb_pages = pages; vcpu_e500->num_shared_tlb_pages = num_pages; @@ -1280,6 +1052,7 @@ int kvm_vcpu_ioctl_dirty_tlb(struct kvm_vcpu *vcpu, int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500) { + struct kvm_vcpu *vcpu = &vcpu_e500->vcpu; int entry_size = sizeof(struct kvm_book3e_206_tlb_entry); int entries = KVM_E500_TLB0_SIZE + KVM_E500_TLB1_SIZE; @@ -1356,20 +1129,17 @@ int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500) if (!vcpu_e500->gtlb_priv[1]) goto err; - if (kvmppc_e500_id_table_alloc(vcpu_e500) == NULL) - goto err; - /* Init TLB configuration register */ - vcpu_e500->tlb0cfg = mfspr(SPRN_TLB0CFG) & + vcpu->arch.tlbcfg[0] = mfspr(SPRN_TLB0CFG) & ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC); - vcpu_e500->tlb0cfg |= vcpu_e500->gtlb_params[0].entries; - vcpu_e500->tlb0cfg |= + vcpu->arch.tlbcfg[0] |= vcpu_e500->gtlb_params[0].entries; + vcpu->arch.tlbcfg[0] |= vcpu_e500->gtlb_params[0].ways << TLBnCFG_ASSOC_SHIFT; - vcpu_e500->tlb1cfg = mfspr(SPRN_TLB1CFG) & + vcpu->arch.tlbcfg[1] = mfspr(SPRN_TLB1CFG) & ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC); - vcpu_e500->tlb0cfg |= vcpu_e500->gtlb_params[1].entries; - vcpu_e500->tlb0cfg |= + vcpu->arch.tlbcfg[0] |= vcpu_e500->gtlb_params[1].entries; + vcpu->arch.tlbcfg[0] |= vcpu_e500->gtlb_params[1].ways << TLBnCFG_ASSOC_SHIFT; return 0; @@ -1384,8 +1154,6 @@ err: void kvmppc_e500_tlb_uninit(struct kvmppc_vcpu_e500 *vcpu_e500) { free_gtlb(vcpu_e500); - kvmppc_e500_id_table_free(vcpu_e500); - kfree(vcpu_e500->tlb_refs[0]); kfree(vcpu_e500->tlb_refs[1]); } -- cgit v1.2.3 From 4f802fe98bd5bd4fe1dd86df3e5c58546e65ad09 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:37 +0000 Subject: KVM: PPC: e500: Track TLB1 entries with a bitmap Rather than invalidate everything when a TLB1 entry needs to be taken down, keep track of which host TLB1 entries are used for a given guest TLB1 entry, and invalidate just those entries. Based on code from Ashish Kalra and Liu Yu . Signed-off-by: Scott Wood Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/e500.h | 5 ++++ arch/powerpc/kvm/e500_tlb.c | 72 +++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 72 insertions(+), 5 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/e500.h b/arch/powerpc/kvm/e500.h index 34cef08f1361..f4dee55ae6c7 100644 --- a/arch/powerpc/kvm/e500.h +++ b/arch/powerpc/kvm/e500.h @@ -2,6 +2,7 @@ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. All rights reserved. * * Author: Yu Liu + * Ashish Kalra * * Description: * This file is based on arch/powerpc/kvm/44x_tlb.h and @@ -25,6 +26,7 @@ #define E500_TLB_VALID 1 #define E500_TLB_DIRTY 2 +#define E500_TLB_BITMAP 4 struct tlbe_ref { pfn_t pfn; @@ -82,6 +84,9 @@ struct kvmppc_vcpu_e500 { struct page **shared_tlb_pages; int num_shared_tlb_pages; + u64 *g2h_tlb1_map; + unsigned int *h2g_tlb1_rmap; + #ifdef CONFIG_KVM_E500 u32 pid[E500_PID_NUM]; diff --git a/arch/powerpc/kvm/e500_tlb.c b/arch/powerpc/kvm/e500_tlb.c index 9925fc6c9cfb..c8ce51d03a2f 100644 --- a/arch/powerpc/kvm/e500_tlb.c +++ b/arch/powerpc/kvm/e500_tlb.c @@ -2,6 +2,7 @@ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. All rights reserved. * * Author: Yu Liu, yu.liu@freescale.com + * Ashish Kalra, ashish.kalra@freescale.com * * Description: * This file is based on arch/powerpc/kvm/44x_tlb.c, @@ -175,8 +176,28 @@ static void inval_gtlbe_on_host(struct kvmppc_vcpu_e500 *vcpu_e500, struct kvm_book3e_206_tlb_entry *gtlbe = get_entry(vcpu_e500, tlbsel, esel); - if (tlbsel == 1) { - kvmppc_e500_tlbil_all(vcpu_e500); + if (tlbsel == 1 && + vcpu_e500->gtlb_priv[1][esel].ref.flags & E500_TLB_BITMAP) { + u64 tmp = vcpu_e500->g2h_tlb1_map[esel]; + int hw_tlb_indx; + unsigned long flags; + + local_irq_save(flags); + while (tmp) { + hw_tlb_indx = __ilog2_u64(tmp & -tmp); + mtspr(SPRN_MAS0, + MAS0_TLBSEL(1) | + MAS0_ESEL(to_htlb1_esel(hw_tlb_indx))); + mtspr(SPRN_MAS1, 0); + asm volatile("tlbwe"); + vcpu_e500->h2g_tlb1_rmap[hw_tlb_indx] = 0; + tmp &= tmp - 1; + } + mb(); + vcpu_e500->g2h_tlb1_map[esel] = 0; + vcpu_e500->gtlb_priv[1][esel].ref.flags &= ~E500_TLB_BITMAP; + local_irq_restore(flags); + return; } @@ -282,6 +303,16 @@ static inline void kvmppc_e500_ref_release(struct tlbe_ref *ref) } } +static void clear_tlb1_bitmap(struct kvmppc_vcpu_e500 *vcpu_e500) +{ + if (vcpu_e500->g2h_tlb1_map) + memset(vcpu_e500->g2h_tlb1_map, + sizeof(u64) * vcpu_e500->gtlb_params[1].entries, 0); + if (vcpu_e500->h2g_tlb1_rmap) + memset(vcpu_e500->h2g_tlb1_rmap, + sizeof(unsigned int) * host_tlb_params[1].entries, 0); +} + static void clear_tlb_privs(struct kvmppc_vcpu_e500 *vcpu_e500) { int tlbsel = 0; @@ -511,7 +542,7 @@ static void kvmppc_e500_tlb0_map(struct kvmppc_vcpu_e500 *vcpu_e500, /* XXX for both one-one and one-to-many , for now use TLB1 */ static int kvmppc_e500_tlb1_map(struct kvmppc_vcpu_e500 *vcpu_e500, u64 gvaddr, gfn_t gfn, struct kvm_book3e_206_tlb_entry *gtlbe, - struct kvm_book3e_206_tlb_entry *stlbe) + struct kvm_book3e_206_tlb_entry *stlbe, int esel) { struct tlbe_ref *ref; unsigned int victim; @@ -524,6 +555,14 @@ static int kvmppc_e500_tlb1_map(struct kvmppc_vcpu_e500 *vcpu_e500, ref = &vcpu_e500->tlb_refs[1][victim]; kvmppc_e500_shadow_map(vcpu_e500, gvaddr, gfn, gtlbe, 1, stlbe, ref); + vcpu_e500->g2h_tlb1_map[esel] |= (u64)1 << victim; + vcpu_e500->gtlb_priv[1][esel].ref.flags |= E500_TLB_BITMAP; + if (vcpu_e500->h2g_tlb1_rmap[victim]) { + unsigned int idx = vcpu_e500->h2g_tlb1_rmap[victim]; + vcpu_e500->g2h_tlb1_map[idx] &= ~(1ULL << victim); + } + vcpu_e500->h2g_tlb1_rmap[victim] = esel; + return victim; } @@ -728,7 +767,7 @@ int kvmppc_e500_emul_tlbwe(struct kvm_vcpu *vcpu) * are mapped on the fly. */ stlbsel = 1; sesel = kvmppc_e500_tlb1_map(vcpu_e500, eaddr, - raddr >> PAGE_SHIFT, gtlbe, &stlbe); + raddr >> PAGE_SHIFT, gtlbe, &stlbe, esel); break; default: @@ -856,7 +895,7 @@ void kvmppc_mmu_map(struct kvm_vcpu *vcpu, u64 eaddr, gpa_t gpaddr, stlbsel = 1; sesel = kvmppc_e500_tlb1_map(vcpu_e500, eaddr, gfn, - gtlbe, &stlbe); + gtlbe, &stlbe, esel); break; } @@ -872,6 +911,9 @@ static void free_gtlb(struct kvmppc_vcpu_e500 *vcpu_e500) { int i; + clear_tlb1_bitmap(vcpu_e500); + kfree(vcpu_e500->g2h_tlb1_map); + clear_tlb_refs(vcpu_e500); kfree(vcpu_e500->gtlb_priv[0]); kfree(vcpu_e500->gtlb_priv[1]); @@ -932,6 +974,7 @@ int kvm_vcpu_ioctl_config_tlb(struct kvm_vcpu *vcpu, char *virt; struct page **pages; struct tlbe_priv *privs[2] = {}; + u64 *g2h_bitmap = NULL; size_t array_len; u32 sets; int num_pages, ret, i; @@ -993,10 +1036,16 @@ int kvm_vcpu_ioctl_config_tlb(struct kvm_vcpu *vcpu, if (!privs[0] || !privs[1]) goto err_put_page; + g2h_bitmap = kzalloc(sizeof(u64) * params.tlb_sizes[1], + GFP_KERNEL); + if (!g2h_bitmap) + goto err_put_page; + free_gtlb(vcpu_e500); vcpu_e500->gtlb_priv[0] = privs[0]; vcpu_e500->gtlb_priv[1] = privs[1]; + vcpu_e500->g2h_tlb1_map = g2h_bitmap; vcpu_e500->gtlb_arch = (struct kvm_book3e_206_tlb_entry *) (virt + (cfg->array & (PAGE_SIZE - 1))); @@ -1129,6 +1178,18 @@ int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500) if (!vcpu_e500->gtlb_priv[1]) goto err; + vcpu_e500->g2h_tlb1_map = kzalloc(sizeof(unsigned int) * + vcpu_e500->gtlb_params[1].entries, + GFP_KERNEL); + if (!vcpu_e500->g2h_tlb1_map) + goto err; + + vcpu_e500->h2g_tlb1_rmap = kzalloc(sizeof(unsigned int) * + host_tlb_params[1].entries, + GFP_KERNEL); + if (!vcpu_e500->h2g_tlb1_rmap) + goto err; + /* Init TLB configuration register */ vcpu->arch.tlbcfg[0] = mfspr(SPRN_TLB0CFG) & ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC); @@ -1154,6 +1215,7 @@ err: void kvmppc_e500_tlb_uninit(struct kvmppc_vcpu_e500 *vcpu_e500) { free_gtlb(vcpu_e500); + kfree(vcpu_e500->h2g_tlb1_rmap); kfree(vcpu_e500->tlb_refs[0]); kfree(vcpu_e500->tlb_refs[1]); } -- cgit v1.2.3 From ab9fc4056af338248640ddb18497be386360363d Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:39 +0000 Subject: KVM: PPC: e500: emulate tlbilx tlbilx is the new, preferred invalidation instruction. It is not found on e500 prior to e500mc, but there should be no harm in supporting it on all e500. Based on code from Ashish Kalra . Signed-off-by: Scott Wood Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/e500.h | 1 + arch/powerpc/kvm/e500_emulate.c | 9 +++++++ arch/powerpc/kvm/e500_tlb.c | 52 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) (limited to 'arch') diff --git a/arch/powerpc/kvm/e500.h b/arch/powerpc/kvm/e500.h index f4dee55ae6c7..ce3f163f702e 100644 --- a/arch/powerpc/kvm/e500.h +++ b/arch/powerpc/kvm/e500.h @@ -124,6 +124,7 @@ int kvmppc_e500_emul_mt_mmucsr0(struct kvmppc_vcpu_e500 *vcpu_e500, int kvmppc_e500_emul_tlbwe(struct kvm_vcpu *vcpu); int kvmppc_e500_emul_tlbre(struct kvm_vcpu *vcpu); int kvmppc_e500_emul_tlbivax(struct kvm_vcpu *vcpu, int ra, int rb); +int kvmppc_e500_emul_tlbilx(struct kvm_vcpu *vcpu, int rt, int ra, int rb); int kvmppc_e500_emul_tlbsx(struct kvm_vcpu *vcpu, int rb); int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500); void kvmppc_e500_tlb_uninit(struct kvmppc_vcpu_e500 *vcpu_e500); diff --git a/arch/powerpc/kvm/e500_emulate.c b/arch/powerpc/kvm/e500_emulate.c index c80794d097d3..af02c18fc798 100644 --- a/arch/powerpc/kvm/e500_emulate.c +++ b/arch/powerpc/kvm/e500_emulate.c @@ -22,6 +22,7 @@ #define XOP_TLBSX 914 #define XOP_TLBRE 946 #define XOP_TLBWE 978 +#define XOP_TLBILX 18 int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu, unsigned int inst, int *advance) @@ -29,6 +30,7 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu, int emulated = EMULATE_DONE; int ra; int rb; + int rt; switch (get_op(inst)) { case 31: @@ -47,6 +49,13 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu, emulated = kvmppc_e500_emul_tlbsx(vcpu,rb); break; + case XOP_TLBILX: + ra = get_ra(inst); + rb = get_rb(inst); + rt = get_rt(inst); + emulated = kvmppc_e500_emul_tlbilx(vcpu, rt, ra, rb); + break; + case XOP_TLBIVAX: ra = get_ra(inst); rb = get_rb(inst); diff --git a/arch/powerpc/kvm/e500_tlb.c b/arch/powerpc/kvm/e500_tlb.c index c8ce51d03a2f..6eb5d655bdb4 100644 --- a/arch/powerpc/kvm/e500_tlb.c +++ b/arch/powerpc/kvm/e500_tlb.c @@ -631,6 +631,58 @@ int kvmppc_e500_emul_tlbivax(struct kvm_vcpu *vcpu, int ra, int rb) return EMULATE_DONE; } +static void tlbilx_all(struct kvmppc_vcpu_e500 *vcpu_e500, int tlbsel, + int pid, int rt) +{ + struct kvm_book3e_206_tlb_entry *tlbe; + int tid, esel; + + /* invalidate all entries */ + for (esel = 0; esel < vcpu_e500->gtlb_params[tlbsel].entries; esel++) { + tlbe = get_entry(vcpu_e500, tlbsel, esel); + tid = get_tlb_tid(tlbe); + if (rt == 0 || tid == pid) { + inval_gtlbe_on_host(vcpu_e500, tlbsel, esel); + kvmppc_e500_gtlbe_invalidate(vcpu_e500, tlbsel, esel); + } + } +} + +static void tlbilx_one(struct kvmppc_vcpu_e500 *vcpu_e500, int pid, + int ra, int rb) +{ + int tlbsel, esel; + gva_t ea; + + ea = kvmppc_get_gpr(&vcpu_e500->vcpu, rb); + if (ra) + ea += kvmppc_get_gpr(&vcpu_e500->vcpu, ra); + + for (tlbsel = 0; tlbsel < 2; tlbsel++) { + esel = kvmppc_e500_tlb_index(vcpu_e500, ea, tlbsel, pid, -1); + if (esel >= 0) { + inval_gtlbe_on_host(vcpu_e500, tlbsel, esel); + kvmppc_e500_gtlbe_invalidate(vcpu_e500, tlbsel, esel); + break; + } + } +} + +int kvmppc_e500_emul_tlbilx(struct kvm_vcpu *vcpu, int rt, int ra, int rb) +{ + struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); + int pid = get_cur_spid(vcpu); + + if (rt == 0 || rt == 1) { + tlbilx_all(vcpu_e500, 0, pid, rt); + tlbilx_all(vcpu_e500, 1, pid, rt); + } else if (rt == 3) { + tlbilx_one(vcpu_e500, pid, ra, rb); + } + + return EMULATE_DONE; +} + int kvmppc_e500_emul_tlbre(struct kvm_vcpu *vcpu) { struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); -- cgit v1.2.3 From cfac57847a67c4903f34a77e971521531bbc7c77 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:40 +0000 Subject: powerpc/booke: Provide exception macros with interrupt name DO_KVM will need to identify the particular exception type. There is an existing set of arbitrary numbers that Linux passes, but it's an undocumented mess that sort of corresponds to server/classic exception vectors but not really. Signed-off-by: Scott Wood Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kernel/head_44x.S | 23 +++++++++------- arch/powerpc/kernel/head_booke.h | 41 +++++++++++++++------------- arch/powerpc/kernel/head_fsl_booke.S | 52 ++++++++++++++++++++++-------------- 3 files changed, 68 insertions(+), 48 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kernel/head_44x.S b/arch/powerpc/kernel/head_44x.S index 7dd2981bcc50..d1192c577ed3 100644 --- a/arch/powerpc/kernel/head_44x.S +++ b/arch/powerpc/kernel/head_44x.S @@ -248,10 +248,11 @@ _ENTRY(_start); interrupt_base: /* Critical Input Interrupt */ - CRITICAL_EXCEPTION(0x0100, CriticalInput, unknown_exception) + CRITICAL_EXCEPTION(0x0100, CRITICAL, CriticalInput, unknown_exception) /* Machine Check Interrupt */ - CRITICAL_EXCEPTION(0x0200, MachineCheck, machine_check_exception) + CRITICAL_EXCEPTION(0x0200, MACHINE_CHECK, MachineCheck, \ + machine_check_exception) MCHECK_EXCEPTION(0x0210, MachineCheckA, machine_check_exception) /* Data Storage Interrupt */ @@ -261,7 +262,8 @@ interrupt_base: INSTRUCTION_STORAGE_EXCEPTION /* External Input Interrupt */ - EXCEPTION(0x0500, ExternalInput, do_IRQ, EXC_XFER_LITE) + EXCEPTION(0x0500, BOOKE_INTERRUPT_EXTERNAL, ExternalInput, \ + do_IRQ, EXC_XFER_LITE) /* Alignment Interrupt */ ALIGNMENT_EXCEPTION @@ -273,29 +275,32 @@ interrupt_base: #ifdef CONFIG_PPC_FPU FP_UNAVAILABLE_EXCEPTION #else - EXCEPTION(0x2010, FloatingPointUnavailable, unknown_exception, EXC_XFER_EE) + EXCEPTION(0x2010, BOOKE_INTERRUPT_FP_UNAVAIL, \ + FloatingPointUnavailable, unknown_exception, EXC_XFER_EE) #endif /* System Call Interrupt */ START_EXCEPTION(SystemCall) - NORMAL_EXCEPTION_PROLOG + NORMAL_EXCEPTION_PROLOG(BOOKE_INTERRUPT_SYSCALL) EXC_XFER_EE_LITE(0x0c00, DoSyscall) /* Auxiliary Processor Unavailable Interrupt */ - EXCEPTION(0x2020, AuxillaryProcessorUnavailable, unknown_exception, EXC_XFER_EE) + EXCEPTION(0x2020, BOOKE_INTERRUPT_AP_UNAVAIL, \ + AuxillaryProcessorUnavailable, unknown_exception, EXC_XFER_EE) /* Decrementer Interrupt */ DECREMENTER_EXCEPTION /* Fixed Internal Timer Interrupt */ /* TODO: Add FIT support */ - EXCEPTION(0x1010, FixedIntervalTimer, unknown_exception, EXC_XFER_EE) + EXCEPTION(0x1010, BOOKE_INTERRUPT_FIT, FixedIntervalTimer, \ + unknown_exception, EXC_XFER_EE) /* Watchdog Timer Interrupt */ /* TODO: Add watchdog support */ #ifdef CONFIG_BOOKE_WDT - CRITICAL_EXCEPTION(0x1020, WatchdogTimer, WatchdogException) + CRITICAL_EXCEPTION(0x1020, WATCHDOG, WatchdogTimer, WatchdogException) #else - CRITICAL_EXCEPTION(0x1020, WatchdogTimer, unknown_exception) + CRITICAL_EXCEPTION(0x1020, WATCHDOG, WatchdogTimer, unknown_exception) #endif /* Data TLB Error Interrupt */ diff --git a/arch/powerpc/kernel/head_booke.h b/arch/powerpc/kernel/head_booke.h index 0e4175388f47..51fd0724e095 100644 --- a/arch/powerpc/kernel/head_booke.h +++ b/arch/powerpc/kernel/head_booke.h @@ -2,6 +2,8 @@ #define __HEAD_BOOKE_H__ #include /* for STACK_FRAME_REGS_MARKER */ +#include + /* * Macros used for common Book-e exception handling */ @@ -28,7 +30,7 @@ */ #define THREAD_NORMSAVE(offset) (THREAD_NORMSAVES + (offset * 4)) -#define NORMAL_EXCEPTION_PROLOG \ +#define NORMAL_EXCEPTION_PROLOG(intno) \ mtspr SPRN_SPRG_WSCRATCH0, r10; /* save one register */ \ mfspr r10, SPRN_SPRG_THREAD; \ stw r11, THREAD_NORMSAVE(0)(r10); \ @@ -113,7 +115,7 @@ * registers as the normal prolog above. Instead we use a portion of the * critical/machine check exception stack at low physical addresses. */ -#define EXC_LEVEL_EXCEPTION_PROLOG(exc_level, exc_level_srr0, exc_level_srr1) \ +#define EXC_LEVEL_EXCEPTION_PROLOG(exc_level, intno, exc_level_srr0, exc_level_srr1) \ mtspr SPRN_SPRG_WSCRATCH_##exc_level,r8; \ BOOKE_LOAD_EXC_LEVEL_STACK(exc_level);/* r8 points to the exc_level stack*/ \ stw r9,GPR9(r8); /* save various registers */\ @@ -162,12 +164,13 @@ SAVE_4GPRS(3, r11); \ SAVE_2GPRS(7, r11) -#define CRITICAL_EXCEPTION_PROLOG \ - EXC_LEVEL_EXCEPTION_PROLOG(CRIT, SPRN_CSRR0, SPRN_CSRR1) +#define CRITICAL_EXCEPTION_PROLOG(intno) \ + EXC_LEVEL_EXCEPTION_PROLOG(CRIT, intno, SPRN_CSRR0, SPRN_CSRR1) #define DEBUG_EXCEPTION_PROLOG \ - EXC_LEVEL_EXCEPTION_PROLOG(DBG, SPRN_DSRR0, SPRN_DSRR1) + EXC_LEVEL_EXCEPTION_PROLOG(DBG, DEBUG, SPRN_DSRR0, SPRN_DSRR1) #define MCHECK_EXCEPTION_PROLOG \ - EXC_LEVEL_EXCEPTION_PROLOG(MC, SPRN_MCSRR0, SPRN_MCSRR1) + EXC_LEVEL_EXCEPTION_PROLOG(MC, MACHINE_CHECK, \ + SPRN_MCSRR0, SPRN_MCSRR1) /* * Exception vectors. @@ -181,16 +184,16 @@ label: .long func; \ .long ret_from_except_full -#define EXCEPTION(n, label, hdlr, xfer) \ +#define EXCEPTION(n, intno, label, hdlr, xfer) \ START_EXCEPTION(label); \ - NORMAL_EXCEPTION_PROLOG; \ + NORMAL_EXCEPTION_PROLOG(intno); \ addi r3,r1,STACK_FRAME_OVERHEAD; \ xfer(n, hdlr) -#define CRITICAL_EXCEPTION(n, label, hdlr) \ - START_EXCEPTION(label); \ - CRITICAL_EXCEPTION_PROLOG; \ - addi r3,r1,STACK_FRAME_OVERHEAD; \ +#define CRITICAL_EXCEPTION(n, intno, label, hdlr) \ + START_EXCEPTION(label); \ + CRITICAL_EXCEPTION_PROLOG(intno); \ + addi r3,r1,STACK_FRAME_OVERHEAD; \ EXC_XFER_TEMPLATE(hdlr, n+2, (MSR_KERNEL & ~(MSR_ME|MSR_DE|MSR_CE)), \ NOCOPY, crit_transfer_to_handler, \ ret_from_crit_exc) @@ -302,7 +305,7 @@ label: #define DEBUG_CRIT_EXCEPTION \ START_EXCEPTION(DebugCrit); \ - CRITICAL_EXCEPTION_PROLOG; \ + CRITICAL_EXCEPTION_PROLOG(DEBUG); \ \ /* \ * If there is a single step or branch-taken exception in an \ @@ -355,7 +358,7 @@ label: #define DATA_STORAGE_EXCEPTION \ START_EXCEPTION(DataStorage) \ - NORMAL_EXCEPTION_PROLOG; \ + NORMAL_EXCEPTION_PROLOG(DATA_STORAGE); \ mfspr r5,SPRN_ESR; /* Grab the ESR and save it */ \ stw r5,_ESR(r11); \ mfspr r4,SPRN_DEAR; /* Grab the DEAR */ \ @@ -363,7 +366,7 @@ label: #define INSTRUCTION_STORAGE_EXCEPTION \ START_EXCEPTION(InstructionStorage) \ - NORMAL_EXCEPTION_PROLOG; \ + NORMAL_EXCEPTION_PROLOG(INST_STORAGE); \ mfspr r5,SPRN_ESR; /* Grab the ESR and save it */ \ stw r5,_ESR(r11); \ mr r4,r12; /* Pass SRR0 as arg2 */ \ @@ -372,7 +375,7 @@ label: #define ALIGNMENT_EXCEPTION \ START_EXCEPTION(Alignment) \ - NORMAL_EXCEPTION_PROLOG; \ + NORMAL_EXCEPTION_PROLOG(ALIGNMENT); \ mfspr r4,SPRN_DEAR; /* Grab the DEAR and save it */ \ stw r4,_DEAR(r11); \ addi r3,r1,STACK_FRAME_OVERHEAD; \ @@ -380,7 +383,7 @@ label: #define PROGRAM_EXCEPTION \ START_EXCEPTION(Program) \ - NORMAL_EXCEPTION_PROLOG; \ + NORMAL_EXCEPTION_PROLOG(PROGRAM); \ mfspr r4,SPRN_ESR; /* Grab the ESR and save it */ \ stw r4,_ESR(r11); \ addi r3,r1,STACK_FRAME_OVERHEAD; \ @@ -388,7 +391,7 @@ label: #define DECREMENTER_EXCEPTION \ START_EXCEPTION(Decrementer) \ - NORMAL_EXCEPTION_PROLOG; \ + NORMAL_EXCEPTION_PROLOG(DECREMENTER); \ lis r0,TSR_DIS@h; /* Setup the DEC interrupt mask */ \ mtspr SPRN_TSR,r0; /* Clear the DEC interrupt */ \ addi r3,r1,STACK_FRAME_OVERHEAD; \ @@ -396,7 +399,7 @@ label: #define FP_UNAVAILABLE_EXCEPTION \ START_EXCEPTION(FloatingPointUnavailable) \ - NORMAL_EXCEPTION_PROLOG; \ + NORMAL_EXCEPTION_PROLOG(FP_UNAVAIL); \ beq 1f; \ bl load_up_fpu; /* if from user, just load it up */ \ b fast_exception_return; \ diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index 28e62598d0e8..7c406dd9fea6 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S @@ -301,19 +301,20 @@ _ENTRY(__early_start) interrupt_base: /* Critical Input Interrupt */ - CRITICAL_EXCEPTION(0x0100, CriticalInput, unknown_exception) + CRITICAL_EXCEPTION(0x0100, CRITICAL, CriticalInput, unknown_exception) /* Machine Check Interrupt */ #ifdef CONFIG_E200 /* no RFMCI, MCSRRs on E200 */ - CRITICAL_EXCEPTION(0x0200, MachineCheck, machine_check_exception) + CRITICAL_EXCEPTION(0x0200, MACHINE_CHECK, MachineCheck, \ + machine_check_exception) #else MCHECK_EXCEPTION(0x0200, MachineCheck, machine_check_exception) #endif /* Data Storage Interrupt */ START_EXCEPTION(DataStorage) - NORMAL_EXCEPTION_PROLOG + NORMAL_EXCEPTION_PROLOG(DATA_STORAGE) mfspr r5,SPRN_ESR /* Grab the ESR, save it, pass arg3 */ stw r5,_ESR(r11) mfspr r4,SPRN_DEAR /* Grab the DEAR, save it, pass arg2 */ @@ -328,7 +329,7 @@ interrupt_base: INSTRUCTION_STORAGE_EXCEPTION /* External Input Interrupt */ - EXCEPTION(0x0500, ExternalInput, do_IRQ, EXC_XFER_LITE) + EXCEPTION(0x0500, EXTERNAL, ExternalInput, do_IRQ, EXC_XFER_LITE) /* Alignment Interrupt */ ALIGNMENT_EXCEPTION @@ -342,32 +343,36 @@ interrupt_base: #else #ifdef CONFIG_E200 /* E200 treats 'normal' floating point instructions as FP Unavail exception */ - EXCEPTION(0x0800, FloatingPointUnavailable, program_check_exception, EXC_XFER_EE) + EXCEPTION(0x0800, FP_UNAVAIL, FloatingPointUnavailable, \ + program_check_exception, EXC_XFER_EE) #else - EXCEPTION(0x0800, FloatingPointUnavailable, unknown_exception, EXC_XFER_EE) + EXCEPTION(0x0800, FP_UNAVAIL, FloatingPointUnavailable, \ + unknown_exception, EXC_XFER_EE) #endif #endif /* System Call Interrupt */ START_EXCEPTION(SystemCall) - NORMAL_EXCEPTION_PROLOG + NORMAL_EXCEPTION_PROLOG(SYSCALL) EXC_XFER_EE_LITE(0x0c00, DoSyscall) /* Auxiliary Processor Unavailable Interrupt */ - EXCEPTION(0x2900, AuxillaryProcessorUnavailable, unknown_exception, EXC_XFER_EE) + EXCEPTION(0x2900, AP_UNAVAIL, AuxillaryProcessorUnavailable, \ + unknown_exception, EXC_XFER_EE) /* Decrementer Interrupt */ DECREMENTER_EXCEPTION /* Fixed Internal Timer Interrupt */ /* TODO: Add FIT support */ - EXCEPTION(0x3100, FixedIntervalTimer, unknown_exception, EXC_XFER_EE) + EXCEPTION(0x3100, FIT, FixedIntervalTimer, \ + unknown_exception, EXC_XFER_EE) /* Watchdog Timer Interrupt */ #ifdef CONFIG_BOOKE_WDT - CRITICAL_EXCEPTION(0x3200, WatchdogTimer, WatchdogException) + CRITICAL_EXCEPTION(0x3200, WATCHDOG, WatchdogTimer, WatchdogException) #else - CRITICAL_EXCEPTION(0x3200, WatchdogTimer, unknown_exception) + CRITICAL_EXCEPTION(0x3200, WATCHDOG, WatchdogTimer, unknown_exception) #endif /* Data TLB Error Interrupt */ @@ -538,31 +543,38 @@ interrupt_base: #ifdef CONFIG_SPE /* SPE Unavailable */ START_EXCEPTION(SPEUnavailable) - NORMAL_EXCEPTION_PROLOG + NORMAL_EXCEPTION_PROLOG(SPE_UNAVAIL) bne load_up_spe addi r3,r1,STACK_FRAME_OVERHEAD EXC_XFER_EE_LITE(0x2010, KernelSPE) #else - EXCEPTION(0x2020, SPEUnavailable, unknown_exception, EXC_XFER_EE) + EXCEPTION(0x2020, SPE_UNAVAIL, SPEUnavailable, \ + unknown_exception, EXC_XFER_EE) #endif /* CONFIG_SPE */ /* SPE Floating Point Data */ #ifdef CONFIG_SPE - EXCEPTION(0x2030, SPEFloatingPointData, SPEFloatingPointException, EXC_XFER_EE); + EXCEPTION(0x2030, SPE_FP_DATA, SPEFloatingPointData, \ + SPEFloatingPointException, EXC_XFER_EE); /* SPE Floating Point Round */ - EXCEPTION(0x2050, SPEFloatingPointRound, SPEFloatingPointRoundException, EXC_XFER_EE) + EXCEPTION(0x2050, SPE_FP_ROUND, SPEFloatingPointRound, \ + SPEFloatingPointRoundException, EXC_XFER_EE) #else - EXCEPTION(0x2040, SPEFloatingPointData, unknown_exception, EXC_XFER_EE) - EXCEPTION(0x2050, SPEFloatingPointRound, unknown_exception, EXC_XFER_EE) + EXCEPTION(0x2040, SPE_FP_DATA, SPEFloatingPointData, \ + unknown_exception, EXC_XFER_EE) + EXCEPTION(0x2050, SPE_FP_ROUND, SPEFloatingPointRound, \ + unknown_exception, EXC_XFER_EE) #endif /* CONFIG_SPE */ /* Performance Monitor */ - EXCEPTION(0x2060, PerformanceMonitor, performance_monitor_exception, EXC_XFER_STD) + EXCEPTION(0x2060, PERFORMANCE_MONITOR, PerformanceMonitor, \ + performance_monitor_exception, EXC_XFER_STD) - EXCEPTION(0x2070, Doorbell, doorbell_exception, EXC_XFER_STD) + EXCEPTION(0x2070, DOORBELL, Doorbell, doorbell_exception, EXC_XFER_STD) - CRITICAL_EXCEPTION(0x2080, CriticalDoorbell, unknown_exception) + CRITICAL_EXCEPTION(0x2080, DOORBELL_CRITICAL, \ + CriticalDoorbell, unknown_exception) /* Debug Interrupt */ DEBUG_DEBUG_EXCEPTION -- cgit v1.2.3 From d30f6e480055e5be12e7a03fd11ea912a451daa5 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:43 +0000 Subject: KVM: PPC: booke: category E.HV (GS-mode) support Chips such as e500mc that implement category E.HV in Power ISA 2.06 provide hardware virtualization features, including a new MSR mode for guest state. The guest OS can perform many operations without trapping into the hypervisor, including transitions to and from guest userspace. Since we can use SRR1[GS] to reliably tell whether an exception came from guest state, instead of messing around with IVPR, we use DO_KVM similarly to book3s. Current issues include: - Machine checks from guest state are not routed to the host handler. - The guest can cause a host oops by executing an emulated instruction in a page that lacks read permission. Existing e500/4xx support has the same problem. Includes work by Ashish Kalra , Varun Sethi , and Liu Yu . Signed-off-by: Scott Wood [agraf: remove pt_regs usage] Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/dbell.h | 1 + arch/powerpc/include/asm/kvm_asm.h | 8 + arch/powerpc/include/asm/kvm_booke_hv_asm.h | 49 +++ arch/powerpc/include/asm/kvm_host.h | 19 +- arch/powerpc/include/asm/kvm_ppc.h | 3 + arch/powerpc/include/asm/mmu-book3e.h | 6 + arch/powerpc/include/asm/processor.h | 3 + arch/powerpc/include/asm/reg.h | 2 + arch/powerpc/include/asm/reg_booke.h | 34 ++ arch/powerpc/kernel/asm-offsets.c | 15 +- arch/powerpc/kernel/head_booke.h | 28 +- arch/powerpc/kvm/Kconfig | 3 + arch/powerpc/kvm/booke.c | 309 ++++++++++++--- arch/powerpc/kvm/booke.h | 24 +- arch/powerpc/kvm/booke_emulate.c | 23 +- arch/powerpc/kvm/bookehv_interrupts.S | 587 ++++++++++++++++++++++++++++ arch/powerpc/kvm/powerpc.c | 5 + arch/powerpc/kvm/timing.h | 6 + 18 files changed, 1058 insertions(+), 67 deletions(-) create mode 100644 arch/powerpc/include/asm/kvm_booke_hv_asm.h create mode 100644 arch/powerpc/kvm/bookehv_interrupts.S (limited to 'arch') diff --git a/arch/powerpc/include/asm/dbell.h b/arch/powerpc/include/asm/dbell.h index efa74ac44a35..d7365b01f0c4 100644 --- a/arch/powerpc/include/asm/dbell.h +++ b/arch/powerpc/include/asm/dbell.h @@ -19,6 +19,7 @@ #define PPC_DBELL_MSG_BRDCAST (0x04000000) #define PPC_DBELL_TYPE(x) (((x) & 0xf) << (63-36)) +#define PPC_DBELL_LPID(x) ((x) << (63 - 49)) enum ppc_dbell { PPC_DBELL = 0, /* doorbell */ PPC_DBELL_CRIT = 1, /* critical doorbell */ diff --git a/arch/powerpc/include/asm/kvm_asm.h b/arch/powerpc/include/asm/kvm_asm.h index 7b1f0e0fc653..097815233284 100644 --- a/arch/powerpc/include/asm/kvm_asm.h +++ b/arch/powerpc/include/asm/kvm_asm.h @@ -48,6 +48,14 @@ #define BOOKE_INTERRUPT_SPE_FP_DATA 33 #define BOOKE_INTERRUPT_SPE_FP_ROUND 34 #define BOOKE_INTERRUPT_PERFORMANCE_MONITOR 35 +#define BOOKE_INTERRUPT_DOORBELL 36 +#define BOOKE_INTERRUPT_DOORBELL_CRITICAL 37 + +/* booke_hv */ +#define BOOKE_INTERRUPT_GUEST_DBELL 38 +#define BOOKE_INTERRUPT_GUEST_DBELL_CRIT 39 +#define BOOKE_INTERRUPT_HV_SYSCALL 40 +#define BOOKE_INTERRUPT_HV_PRIV 41 /* book3s */ diff --git a/arch/powerpc/include/asm/kvm_booke_hv_asm.h b/arch/powerpc/include/asm/kvm_booke_hv_asm.h new file mode 100644 index 000000000000..30a600fa1b6a --- /dev/null +++ b/arch/powerpc/include/asm/kvm_booke_hv_asm.h @@ -0,0 +1,49 @@ +/* + * Copyright 2010-2011 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + */ + +#ifndef ASM_KVM_BOOKE_HV_ASM_H +#define ASM_KVM_BOOKE_HV_ASM_H + +#ifdef __ASSEMBLY__ + +/* + * All exceptions from guest state must go through KVM + * (except for those which are delivered directly to the guest) -- + * there are no exceptions for which we fall through directly to + * the normal host handler. + * + * Expected inputs (normal exceptions): + * SCRATCH0 = saved r10 + * r10 = thread struct + * r11 = appropriate SRR1 variant (currently used as scratch) + * r13 = saved CR + * *(r10 + THREAD_NORMSAVE(0)) = saved r11 + * *(r10 + THREAD_NORMSAVE(2)) = saved r13 + * + * Expected inputs (crit/mcheck/debug exceptions): + * appropriate SCRATCH = saved r8 + * r8 = exception level stack frame + * r9 = *(r8 + _CCR) = saved CR + * r11 = appropriate SRR1 variant (currently used as scratch) + * *(r8 + GPR9) = saved r9 + * *(r8 + GPR10) = saved r10 (r10 not yet clobbered) + * *(r8 + GPR11) = saved r11 + */ +.macro DO_KVM intno srr1 +#ifdef CONFIG_KVM_BOOKE_HV +BEGIN_FTR_SECTION + mtocrf 0x80, r11 /* check MSR[GS] without clobbering reg */ + bf 3, kvmppc_resume_\intno\()_\srr1 + b kvmppc_handler_\intno\()_\srr1 +kvmppc_resume_\intno\()_\srr1: +END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV) +#endif +.endm + +#endif /*__ASSEMBLY__ */ +#endif /* ASM_KVM_BOOKE_HV_ASM_H */ diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 5b81cbc43a42..e645623728fc 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -106,6 +106,8 @@ struct kvm_vcpu_stat { u32 dec_exits; u32 ext_intr_exits; u32 halt_wakeup; + u32 dbell_exits; + u32 gdbell_exits; #ifdef CONFIG_PPC_BOOK3S u32 pf_storage; u32 pf_instruc; @@ -140,6 +142,7 @@ enum kvm_exit_types { EMULATED_TLBSX_EXITS, EMULATED_TLBWE_EXITS, EMULATED_RFI_EXITS, + EMULATED_RFCI_EXITS, DEC_EXITS, EXT_INTR_EXITS, HALT_WAKEUP, @@ -147,6 +150,8 @@ enum kvm_exit_types { FP_UNAVAIL, DEBUG_EXITS, TIMEINGUEST, + DBELL_EXITS, + GDBELL_EXITS, __NUMBER_OF_KVM_EXIT_TYPES }; @@ -217,10 +222,10 @@ struct kvm_arch_memory_slot { }; struct kvm_arch { + unsigned int lpid; #ifdef CONFIG_KVM_BOOK3S_64_HV unsigned long hpt_virt; struct revmap_entry *revmap; - unsigned int lpid; unsigned int host_lpid; unsigned long host_lpcr; unsigned long sdr1; @@ -345,6 +350,17 @@ struct kvm_vcpu_arch { u64 vsr[64]; #endif +#ifdef CONFIG_KVM_BOOKE_HV + u32 host_mas4; + u32 host_mas6; + u32 shadow_epcr; + u32 epcr; + u32 shadow_msrp; + u32 eplc; + u32 epsc; + u32 oldpir; +#endif + #ifdef CONFIG_PPC_BOOK3S /* For Gekko paired singles */ u32 qpr[32]; @@ -428,6 +444,7 @@ struct kvm_vcpu_arch { ulong queued_esr; u32 tlbcfg[4]; u32 mmucfg; + u32 epr; #endif gpa_t paddr_accessed; diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index 731e920eda1e..e709975702a6 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -139,6 +139,9 @@ extern int kvmppc_core_prepare_memory_region(struct kvm *kvm, extern void kvmppc_core_commit_memory_region(struct kvm *kvm, struct kvm_userspace_memory_region *mem); +extern int kvmppc_bookehv_init(void); +extern void kvmppc_bookehv_exit(void); + /* * Cuts out inst bits with ordering according to spec. * That means the leftmost bit is zero. All given bits are included. diff --git a/arch/powerpc/include/asm/mmu-book3e.h b/arch/powerpc/include/asm/mmu-book3e.h index cdb5421877e2..eeabcdbc30f7 100644 --- a/arch/powerpc/include/asm/mmu-book3e.h +++ b/arch/powerpc/include/asm/mmu-book3e.h @@ -104,6 +104,8 @@ #define MAS4_TSIZED_MASK 0x00000f80 /* Default TSIZE */ #define MAS4_TSIZED_SHIFT 7 +#define MAS5_SGS 0x80000000 + #define MAS6_SPID0 0x3FFF0000 #define MAS6_SPID1 0x00007FFE #define MAS6_ISIZE(x) MAS1_TSIZE(x) @@ -118,6 +120,10 @@ #define MAS7_RPN 0xFFFFFFFF +#define MAS8_TGS 0x80000000 /* Guest space */ +#define MAS8_VF 0x40000000 /* Virtualization Fault */ +#define MAS8_TLPID 0x000000ff + /* Bit definitions for MMUCFG */ #define MMUCFG_MAVN 0x00000003 /* MMU Architecture Version Number */ #define MMUCFG_MAVN_V1 0x00000000 /* v1.0 */ diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h index 8e2d0371fe1e..2a25ab0f5896 100644 --- a/arch/powerpc/include/asm/processor.h +++ b/arch/powerpc/include/asm/processor.h @@ -243,6 +243,9 @@ struct thread_struct { #ifdef CONFIG_KVM_BOOK3S_32_HANDLER void* kvm_shadow_vcpu; /* KVM internal data */ #endif /* CONFIG_KVM_BOOK3S_32_HANDLER */ +#if defined(CONFIG_KVM) && defined(CONFIG_BOOKE) + struct kvm_vcpu *kvm_vcpu; +#endif #ifdef CONFIG_PPC64 unsigned long dscr; int dscr_inherit; diff --git a/arch/powerpc/include/asm/reg.h b/arch/powerpc/include/asm/reg.h index 9d7f0fb69028..f0cb7f461b9d 100644 --- a/arch/powerpc/include/asm/reg.h +++ b/arch/powerpc/include/asm/reg.h @@ -257,7 +257,9 @@ #define LPCR_LPES_SH 2 #define LPCR_RMI 0x00000002 /* real mode is cache inhibit */ #define LPCR_HDICE 0x00000001 /* Hyp Decr enable (HV,PR,EE) */ +#ifndef SPRN_LPID #define SPRN_LPID 0x13F /* Logical Partition Identifier */ +#endif #define LPID_RSVD 0x3ff /* Reserved LPID for partn switching */ #define SPRN_HMER 0x150 /* Hardware m? error recovery */ #define SPRN_HMEER 0x151 /* Hardware m? enable error recovery */ diff --git a/arch/powerpc/include/asm/reg_booke.h b/arch/powerpc/include/asm/reg_booke.h index b86faa9107da..815e404f8c18 100644 --- a/arch/powerpc/include/asm/reg_booke.h +++ b/arch/powerpc/include/asm/reg_booke.h @@ -61,18 +61,30 @@ extern u32 booke_wdt_period; #define SPRN_SPRG7W 0x117 /* Special Purpose Register General 7 Write */ #define SPRN_EPCR 0x133 /* Embedded Processor Control Register */ #define SPRN_DBCR2 0x136 /* Debug Control Register 2 */ +#define SPRN_MSRP 0x137 /* MSR Protect Register */ #define SPRN_IAC3 0x13A /* Instruction Address Compare 3 */ #define SPRN_IAC4 0x13B /* Instruction Address Compare 4 */ #define SPRN_DVC1 0x13E /* Data Value Compare Register 1 */ #define SPRN_DVC2 0x13F /* Data Value Compare Register 2 */ +#define SPRN_LPID 0x152 /* Logical Partition ID */ #define SPRN_MAS8 0x155 /* MMU Assist Register 8 */ #define SPRN_TLB0PS 0x158 /* TLB 0 Page Size Register */ #define SPRN_TLB1PS 0x159 /* TLB 1 Page Size Register */ #define SPRN_MAS5_MAS6 0x15c /* MMU Assist Register 5 || 6 */ #define SPRN_MAS8_MAS1 0x15d /* MMU Assist Register 8 || 1 */ #define SPRN_EPTCFG 0x15e /* Embedded Page Table Config */ +#define SPRN_GSPRG0 0x170 /* Guest SPRG0 */ +#define SPRN_GSPRG1 0x171 /* Guest SPRG1 */ +#define SPRN_GSPRG2 0x172 /* Guest SPRG2 */ +#define SPRN_GSPRG3 0x173 /* Guest SPRG3 */ #define SPRN_MAS7_MAS3 0x174 /* MMU Assist Register 7 || 3 */ #define SPRN_MAS0_MAS1 0x175 /* MMU Assist Register 0 || 1 */ +#define SPRN_GSRR0 0x17A /* Guest SRR0 */ +#define SPRN_GSRR1 0x17B /* Guest SRR1 */ +#define SPRN_GEPR 0x17C /* Guest EPR */ +#define SPRN_GDEAR 0x17D /* Guest DEAR */ +#define SPRN_GPIR 0x17E /* Guest PIR */ +#define SPRN_GESR 0x17F /* Guest Exception Syndrome Register */ #define SPRN_IVOR0 0x190 /* Interrupt Vector Offset Register 0 */ #define SPRN_IVOR1 0x191 /* Interrupt Vector Offset Register 1 */ #define SPRN_IVOR2 0x192 /* Interrupt Vector Offset Register 2 */ @@ -93,6 +105,13 @@ extern u32 booke_wdt_period; #define SPRN_IVOR39 0x1B1 /* Interrupt Vector Offset Register 39 */ #define SPRN_IVOR40 0x1B2 /* Interrupt Vector Offset Register 40 */ #define SPRN_IVOR41 0x1B3 /* Interrupt Vector Offset Register 41 */ +#define SPRN_GIVOR2 0x1B8 /* Guest IVOR2 */ +#define SPRN_GIVOR3 0x1B9 /* Guest IVOR3 */ +#define SPRN_GIVOR4 0x1BA /* Guest IVOR4 */ +#define SPRN_GIVOR8 0x1BB /* Guest IVOR8 */ +#define SPRN_GIVOR13 0x1BC /* Guest IVOR13 */ +#define SPRN_GIVOR14 0x1BD /* Guest IVOR14 */ +#define SPRN_GIVPR 0x1BF /* Guest IVPR */ #define SPRN_SPEFSCR 0x200 /* SPE & Embedded FP Status & Control */ #define SPRN_BBEAR 0x201 /* Branch Buffer Entry Address Register */ #define SPRN_BBTAR 0x202 /* Branch Buffer Target Address Register */ @@ -245,6 +264,10 @@ extern u32 booke_wdt_period; #define MCSR_LDG 0x00002000UL /* Guarded Load */ #define MCSR_TLBSYNC 0x00000002UL /* Multiple tlbsyncs detected */ #define MCSR_BSL2_ERR 0x00000001UL /* Backside L2 cache error */ + +#define MSRP_UCLEP 0x04000000 /* Protect MSR[UCLE] */ +#define MSRP_DEP 0x00000200 /* Protect MSR[DE] */ +#define MSRP_PMMP 0x00000004 /* Protect MSR[PMM] */ #endif #ifdef CONFIG_E200 @@ -599,6 +622,17 @@ extern u32 booke_wdt_period; #define SPRN_EPCR_DMIUH 0x00400000 /* Disable MAS Interrupt updates * for hypervisor */ +/* Bit definitions for EPLC/EPSC */ +#define EPC_EPR 0x80000000 /* 1 = user, 0 = kernel */ +#define EPC_EPR_SHIFT 31 +#define EPC_EAS 0x40000000 /* Address Space */ +#define EPC_EAS_SHIFT 30 +#define EPC_EGS 0x20000000 /* 1 = guest, 0 = hypervisor */ +#define EPC_EGS_SHIFT 29 +#define EPC_ELPID 0x00ff0000 +#define EPC_ELPID_SHIFT 16 +#define EPC_EPID 0x00003fff +#define EPC_EPID_SHIFT 0 /* * The IBM-403 is an even more odd special case, as it is much diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 34b8afe94a50..bbede5882c5b 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -116,6 +116,9 @@ int main(void) #ifdef CONFIG_KVM_BOOK3S_32_HANDLER DEFINE(THREAD_KVM_SVCPU, offsetof(struct thread_struct, kvm_shadow_vcpu)); #endif +#ifdef CONFIG_KVM_BOOKE_HV + DEFINE(THREAD_KVM_VCPU, offsetof(struct thread_struct, kvm_vcpu)); +#endif DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); DEFINE(TI_LOCAL_FLAGS, offsetof(struct thread_info, local_flags)); @@ -387,6 +390,7 @@ int main(void) #ifdef CONFIG_KVM DEFINE(VCPU_HOST_STACK, offsetof(struct kvm_vcpu, arch.host_stack)); DEFINE(VCPU_HOST_PID, offsetof(struct kvm_vcpu, arch.host_pid)); + DEFINE(VCPU_GUEST_PID, offsetof(struct kvm_vcpu, arch.pid)); DEFINE(VCPU_GPRS, offsetof(struct kvm_vcpu, arch.gpr)); DEFINE(VCPU_VRSAVE, offsetof(struct kvm_vcpu, arch.vrsave)); DEFINE(VCPU_FPRS, offsetof(struct kvm_vcpu, arch.fpr)); @@ -429,9 +433,11 @@ int main(void) DEFINE(VCPU_SHARED_MAS4, offsetof(struct kvm_vcpu_arch_shared, mas4)); DEFINE(VCPU_SHARED_MAS6, offsetof(struct kvm_vcpu_arch_shared, mas6)); + DEFINE(VCPU_KVM, offsetof(struct kvm_vcpu, kvm)); + DEFINE(KVM_LPID, offsetof(struct kvm, arch.lpid)); + /* book3s */ #ifdef CONFIG_KVM_BOOK3S_64_HV - DEFINE(KVM_LPID, offsetof(struct kvm, arch.lpid)); DEFINE(KVM_SDR1, offsetof(struct kvm, arch.sdr1)); DEFINE(KVM_HOST_LPID, offsetof(struct kvm, arch.host_lpid)); DEFINE(KVM_HOST_LPCR, offsetof(struct kvm, arch.host_lpcr)); @@ -446,7 +452,6 @@ int main(void) DEFINE(VCPU_DAR, offsetof(struct kvm_vcpu, arch.shregs.dar)); #endif #ifdef CONFIG_PPC_BOOK3S - DEFINE(VCPU_KVM, offsetof(struct kvm_vcpu, kvm)); DEFINE(VCPU_VCPUID, offsetof(struct kvm_vcpu, vcpu_id)); DEFINE(VCPU_PURR, offsetof(struct kvm_vcpu, arch.purr)); DEFINE(VCPU_SPURR, offsetof(struct kvm_vcpu, arch.spurr)); @@ -597,6 +602,12 @@ int main(void) DEFINE(VCPU_HOST_SPEFSCR, offsetof(struct kvm_vcpu, arch.host_spefscr)); #endif +#ifdef CONFIG_KVM_BOOKE_HV + DEFINE(VCPU_HOST_MAS4, offsetof(struct kvm_vcpu, arch.host_mas4)); + DEFINE(VCPU_HOST_MAS6, offsetof(struct kvm_vcpu, arch.host_mas6)); + DEFINE(VCPU_EPLC, offsetof(struct kvm_vcpu, arch.eplc)); +#endif + #ifdef CONFIG_KVM_EXIT_TIMING DEFINE(VCPU_TIMING_EXIT_TBU, offsetof(struct kvm_vcpu, arch.timing_exit.tv32.tbu)); diff --git a/arch/powerpc/kernel/head_booke.h b/arch/powerpc/kernel/head_booke.h index 51fd0724e095..5f051eeb93a2 100644 --- a/arch/powerpc/kernel/head_booke.h +++ b/arch/powerpc/kernel/head_booke.h @@ -3,6 +3,7 @@ #include /* for STACK_FRAME_REGS_MARKER */ #include +#include /* * Macros used for common Book-e exception handling @@ -36,8 +37,9 @@ stw r11, THREAD_NORMSAVE(0)(r10); \ stw r13, THREAD_NORMSAVE(2)(r10); \ mfcr r13; /* save CR in r13 for now */\ - mfspr r11,SPRN_SRR1; /* check whether user or kernel */\ - andi. r11,r11,MSR_PR; \ + mfspr r11, SPRN_SRR1; \ + DO_KVM BOOKE_INTERRUPT_##intno SPRN_SRR1; \ + andi. r11, r11, MSR_PR; /* check whether user or kernel */\ mr r11, r1; \ beq 1f; \ /* if from user, start at top of this thread's kernel stack */ \ @@ -123,8 +125,9 @@ stw r10,GPR10(r8); \ stw r11,GPR11(r8); \ stw r9,_CCR(r8); /* save CR on stack */\ - mfspr r10,exc_level_srr1; /* check whether user or kernel */\ - andi. r10,r10,MSR_PR; \ + mfspr r11,exc_level_srr1; /* check whether user or kernel */\ + DO_KVM BOOKE_INTERRUPT_##intno exc_level_srr1; \ + andi. r11,r11,MSR_PR; \ mfspr r11,SPRN_SPRG_THREAD; /* if from user, start at top of */\ lwz r11,THREAD_INFO-THREAD(r11); /* this thread's kernel stack */\ addi r11,r11,EXC_LVL_FRAME_OVERHEAD; /* allocate stack frame */\ @@ -172,6 +175,23 @@ EXC_LEVEL_EXCEPTION_PROLOG(MC, MACHINE_CHECK, \ SPRN_MCSRR0, SPRN_MCSRR1) +/* + * Guest Doorbell -- this is a bit odd in that uses GSRR0/1 despite + * being delivered to the host. This exception can only happen + * inside a KVM guest -- so we just handle up to the DO_KVM rather + * than try to fit this into one of the existing prolog macros. + */ +#define GUEST_DOORBELL_EXCEPTION \ + START_EXCEPTION(GuestDoorbell); \ + mtspr SPRN_SPRG_WSCRATCH0, r10; /* save one register */ \ + mfspr r10, SPRN_SPRG_THREAD; \ + stw r11, THREAD_NORMSAVE(0)(r10); \ + mfspr r11, SPRN_SRR1; \ + stw r13, THREAD_NORMSAVE(2)(r10); \ + mfcr r13; /* save CR in r13 for now */\ + DO_KVM BOOKE_INTERRUPT_GUEST_DBELL SPRN_GSRR1; \ + trap + /* * Exception vectors. */ diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig index 8f64709ae331..2c33cd336434 100644 --- a/arch/powerpc/kvm/Kconfig +++ b/arch/powerpc/kvm/Kconfig @@ -90,6 +90,9 @@ config KVM_BOOK3S_64_PR depends on KVM_BOOK3S_64 && !KVM_BOOK3S_64_HV select KVM_BOOK3S_PR +config KVM_BOOKE_HV + bool + config KVM_440 bool "KVM support for PowerPC 440 processors" depends on EXPERIMENTAL && 44x diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 2ee9bae38328..75dbaeb2efa3 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -17,6 +17,8 @@ * * Authors: Hollis Blanchard * Christian Ehrhardt + * Scott Wood + * Varun Sethi */ #include @@ -30,9 +32,12 @@ #include #include #include -#include "timing.h" #include +#include +#include +#include +#include "timing.h" #include "booke.h" unsigned long kvmppc_booke_handlers; @@ -55,6 +60,8 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { { "dec", VCPU_STAT(dec_exits) }, { "ext_intr", VCPU_STAT(ext_intr_exits) }, { "halt_wakeup", VCPU_STAT(halt_wakeup) }, + { "doorbell", VCPU_STAT(dbell_exits) }, + { "guest doorbell", VCPU_STAT(gdbell_exits) }, { NULL } }; @@ -121,6 +128,10 @@ void kvmppc_set_msr(struct kvm_vcpu *vcpu, u32 new_msr) { u32 old_msr = vcpu->arch.shared->msr; +#ifdef CONFIG_KVM_BOOKE_HV + new_msr |= MSR_GS; +#endif + vcpu->arch.shared->msr = new_msr; kvmppc_mmu_msr_notify(vcpu, old_msr); @@ -195,6 +206,75 @@ void kvmppc_core_dequeue_external(struct kvm_vcpu *vcpu, clear_bit(BOOKE_IRQPRIO_EXTERNAL_LEVEL, &vcpu->arch.pending_exceptions); } +static void set_guest_srr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1) +{ +#ifdef CONFIG_KVM_BOOKE_HV + mtspr(SPRN_GSRR0, srr0); + mtspr(SPRN_GSRR1, srr1); +#else + vcpu->arch.shared->srr0 = srr0; + vcpu->arch.shared->srr1 = srr1; +#endif +} + +static void set_guest_csrr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1) +{ + vcpu->arch.csrr0 = srr0; + vcpu->arch.csrr1 = srr1; +} + +static void set_guest_dsrr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1) +{ + if (cpu_has_feature(CPU_FTR_DEBUG_LVL_EXC)) { + vcpu->arch.dsrr0 = srr0; + vcpu->arch.dsrr1 = srr1; + } else { + set_guest_csrr(vcpu, srr0, srr1); + } +} + +static void set_guest_mcsrr(struct kvm_vcpu *vcpu, unsigned long srr0, u32 srr1) +{ + vcpu->arch.mcsrr0 = srr0; + vcpu->arch.mcsrr1 = srr1; +} + +static unsigned long get_guest_dear(struct kvm_vcpu *vcpu) +{ +#ifdef CONFIG_KVM_BOOKE_HV + return mfspr(SPRN_GDEAR); +#else + return vcpu->arch.shared->dar; +#endif +} + +static void set_guest_dear(struct kvm_vcpu *vcpu, unsigned long dear) +{ +#ifdef CONFIG_KVM_BOOKE_HV + mtspr(SPRN_GDEAR, dear); +#else + vcpu->arch.shared->dar = dear; +#endif +} + +static unsigned long get_guest_esr(struct kvm_vcpu *vcpu) +{ +#ifdef CONFIG_KVM_BOOKE_HV + return mfspr(SPRN_GESR); +#else + return vcpu->arch.shared->esr; +#endif +} + +static void set_guest_esr(struct kvm_vcpu *vcpu, u32 esr) +{ +#ifdef CONFIG_KVM_BOOKE_HV + mtspr(SPRN_GESR, esr); +#else + vcpu->arch.shared->esr = esr; +#endif +} + /* Deliver the interrupt of the corresponding priority, if possible. */ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, unsigned int priority) @@ -206,6 +286,7 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, ulong crit_r1 = kvmppc_get_gpr(vcpu, 1); bool crit; bool keep_irq = false; + enum int_class int_class; /* Truncate crit indicators in 32 bit mode */ if (!(vcpu->arch.shared->msr & MSR_SF)) { @@ -241,16 +322,20 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, case BOOKE_IRQPRIO_AP_UNAVAIL: case BOOKE_IRQPRIO_ALIGNMENT: allowed = 1; - msr_mask = MSR_CE|MSR_ME|MSR_DE; + msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE; + int_class = INT_CLASS_NONCRIT; break; case BOOKE_IRQPRIO_CRITICAL: - case BOOKE_IRQPRIO_WATCHDOG: allowed = vcpu->arch.shared->msr & MSR_CE; - msr_mask = MSR_ME; + allowed = allowed && !crit; + msr_mask = MSR_GS | MSR_ME; + int_class = INT_CLASS_CRIT; break; case BOOKE_IRQPRIO_MACHINE_CHECK: allowed = vcpu->arch.shared->msr & MSR_ME; - msr_mask = 0; + allowed = allowed && !crit; + msr_mask = MSR_GS; + int_class = INT_CLASS_MC; break; case BOOKE_IRQPRIO_DECREMENTER: case BOOKE_IRQPRIO_FIT: @@ -259,28 +344,62 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, case BOOKE_IRQPRIO_EXTERNAL: allowed = vcpu->arch.shared->msr & MSR_EE; allowed = allowed && !crit; - msr_mask = MSR_CE|MSR_ME|MSR_DE; + msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE; + int_class = INT_CLASS_NONCRIT; break; case BOOKE_IRQPRIO_DEBUG: allowed = vcpu->arch.shared->msr & MSR_DE; - msr_mask = MSR_ME; + allowed = allowed && !crit; + msr_mask = MSR_GS | MSR_ME; + int_class = INT_CLASS_CRIT; break; } if (allowed) { - vcpu->arch.shared->srr0 = vcpu->arch.pc; - vcpu->arch.shared->srr1 = vcpu->arch.shared->msr; + switch (int_class) { + case INT_CLASS_NONCRIT: + set_guest_srr(vcpu, vcpu->arch.pc, + vcpu->arch.shared->msr); + break; + case INT_CLASS_CRIT: + set_guest_csrr(vcpu, vcpu->arch.pc, + vcpu->arch.shared->msr); + break; + case INT_CLASS_DBG: + set_guest_dsrr(vcpu, vcpu->arch.pc, + vcpu->arch.shared->msr); + break; + case INT_CLASS_MC: + set_guest_mcsrr(vcpu, vcpu->arch.pc, + vcpu->arch.shared->msr); + break; + } + vcpu->arch.pc = vcpu->arch.ivpr | vcpu->arch.ivor[priority]; if (update_esr == true) - vcpu->arch.shared->esr = vcpu->arch.queued_esr; + set_guest_esr(vcpu, vcpu->arch.queued_esr); if (update_dear == true) - vcpu->arch.shared->dar = vcpu->arch.queued_dear; + set_guest_dear(vcpu, vcpu->arch.queued_dear); kvmppc_set_msr(vcpu, vcpu->arch.shared->msr & msr_mask); if (!keep_irq) clear_bit(priority, &vcpu->arch.pending_exceptions); } +#ifdef CONFIG_KVM_BOOKE_HV + /* + * If an interrupt is pending but masked, raise a guest doorbell + * so that we are notified when the guest enables the relevant + * MSR bit. + */ + if (vcpu->arch.pending_exceptions & BOOKE_IRQMASK_EE) + kvmppc_set_pending_interrupt(vcpu, INT_CLASS_NONCRIT); + if (vcpu->arch.pending_exceptions & BOOKE_IRQMASK_CE) + kvmppc_set_pending_interrupt(vcpu, INT_CLASS_CRIT); + if (vcpu->arch.pending_exceptions & BOOKE_IRQPRIO_MACHINE_CHECK) + kvmppc_set_pending_interrupt(vcpu, INT_CLASS_MC); +#endif + return allowed; } @@ -344,6 +463,11 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) return -EINVAL; } + if (!current->thread.kvm_vcpu) { + WARN(1, "no vcpu\n"); + return -EPERM; + } + local_irq_disable(); kvmppc_core_prepare_to_enter(vcpu); @@ -363,6 +487,38 @@ out: return ret; } +static int emulation_exit(struct kvm_run *run, struct kvm_vcpu *vcpu) +{ + enum emulation_result er; + + er = kvmppc_emulate_instruction(run, vcpu); + switch (er) { + case EMULATE_DONE: + /* don't overwrite subtypes, just account kvm_stats */ + kvmppc_account_exit_stat(vcpu, EMULATED_INST_EXITS); + /* Future optimization: only reload non-volatiles if + * they were actually modified by emulation. */ + return RESUME_GUEST_NV; + + case EMULATE_DO_DCR: + run->exit_reason = KVM_EXIT_DCR; + return RESUME_HOST; + + case EMULATE_FAIL: + /* XXX Deliver Program interrupt to guest. */ + printk(KERN_CRIT "%s: emulation at %lx failed (%08x)\n", + __func__, vcpu->arch.pc, vcpu->arch.last_inst); + /* For debugging, encode the failing instruction and + * report it to userspace. */ + run->hw.hardware_exit_reason = ~0ULL << 32; + run->hw.hardware_exit_reason |= vcpu->arch.last_inst; + return RESUME_HOST; + + default: + BUG(); + } +} + /** * kvmppc_handle_exit * @@ -371,12 +527,30 @@ out: int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, unsigned int exit_nr) { - enum emulation_result er; int r = RESUME_HOST; /* update before a new last_exit_type is rewritten */ kvmppc_update_timing_stats(vcpu); + switch (exit_nr) { + case BOOKE_INTERRUPT_EXTERNAL: + do_IRQ(current->thread.regs); + break; + + case BOOKE_INTERRUPT_DECREMENTER: + timer_interrupt(current->thread.regs); + break; + +#if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3E_64) + case BOOKE_INTERRUPT_DOORBELL: + doorbell_exception(current->thread.regs); + break; +#endif + case BOOKE_INTERRUPT_MACHINE_CHECK: + /* FIXME */ + break; + } + local_irq_enable(); run->exit_reason = KVM_EXIT_UNKNOWN; @@ -384,30 +558,56 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, switch (exit_nr) { case BOOKE_INTERRUPT_MACHINE_CHECK: - printk("MACHINE CHECK: %lx\n", mfspr(SPRN_MCSR)); - kvmppc_dump_vcpu(vcpu); - r = RESUME_HOST; + kvm_resched(vcpu); + r = RESUME_GUEST; break; case BOOKE_INTERRUPT_EXTERNAL: kvmppc_account_exit(vcpu, EXT_INTR_EXITS); - if (need_resched()) - cond_resched(); + kvm_resched(vcpu); r = RESUME_GUEST; break; case BOOKE_INTERRUPT_DECREMENTER: - /* Since we switched IVPR back to the host's value, the host - * handled this interrupt the moment we enabled interrupts. - * Now we just offer it a chance to reschedule the guest. */ kvmppc_account_exit(vcpu, DEC_EXITS); - if (need_resched()) - cond_resched(); + kvm_resched(vcpu); r = RESUME_GUEST; break; + case BOOKE_INTERRUPT_DOORBELL: + kvmppc_account_exit(vcpu, DBELL_EXITS); + kvm_resched(vcpu); + r = RESUME_GUEST; + break; + + case BOOKE_INTERRUPT_GUEST_DBELL_CRIT: + kvmppc_account_exit(vcpu, GDBELL_EXITS); + + /* + * We are here because there is a pending guest interrupt + * which could not be delivered as MSR_CE or MSR_ME was not + * set. Once we break from here we will retry delivery. + */ + r = RESUME_GUEST; + break; + + case BOOKE_INTERRUPT_GUEST_DBELL: + kvmppc_account_exit(vcpu, GDBELL_EXITS); + + /* + * We are here because there is a pending guest interrupt + * which could not be delivered as MSR_EE was not set. Once + * we break from here we will retry delivery. + */ + r = RESUME_GUEST; + break; + + case BOOKE_INTERRUPT_HV_PRIV: + r = emulation_exit(run, vcpu); + break; + case BOOKE_INTERRUPT_PROGRAM: - if (vcpu->arch.shared->msr & MSR_PR) { + if (vcpu->arch.shared->msr & (MSR_PR | MSR_GS)) { /* Program traps generated by user-level software must be handled * by the guest kernel. */ kvmppc_core_queue_program(vcpu, vcpu->arch.fault_esr); @@ -416,32 +616,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, break; } - er = kvmppc_emulate_instruction(run, vcpu); - switch (er) { - case EMULATE_DONE: - /* don't overwrite subtypes, just account kvm_stats */ - kvmppc_account_exit_stat(vcpu, EMULATED_INST_EXITS); - /* Future optimization: only reload non-volatiles if - * they were actually modified by emulation. */ - r = RESUME_GUEST_NV; - break; - case EMULATE_DO_DCR: - run->exit_reason = KVM_EXIT_DCR; - r = RESUME_HOST; - break; - case EMULATE_FAIL: - /* XXX Deliver Program interrupt to guest. */ - printk(KERN_CRIT "%s: emulation at %lx failed (%08x)\n", - __func__, vcpu->arch.pc, vcpu->arch.last_inst); - /* For debugging, encode the failing instruction and - * report it to userspace. */ - run->hw.hardware_exit_reason = ~0ULL << 32; - run->hw.hardware_exit_reason |= vcpu->arch.last_inst; - r = RESUME_HOST; - break; - default: - BUG(); - } + r = emulation_exit(run, vcpu); break; case BOOKE_INTERRUPT_FP_UNAVAIL: @@ -506,6 +681,21 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, r = RESUME_GUEST; break; +#ifdef CONFIG_KVM_BOOKE_HV + case BOOKE_INTERRUPT_HV_SYSCALL: + if (!(vcpu->arch.shared->msr & MSR_PR)) { + kvmppc_set_gpr(vcpu, 3, kvmppc_kvm_pv(vcpu)); + } else { + /* + * hcall from guest userspace -- send privileged + * instruction program check. + */ + kvmppc_core_queue_program(vcpu, ESR_PPR); + } + + r = RESUME_GUEST; + break; +#else case BOOKE_INTERRUPT_SYSCALL: if (!(vcpu->arch.shared->msr & MSR_PR) && (((u32)kvmppc_get_gpr(vcpu, 0)) == KVM_SC_MAGIC_R0)) { @@ -519,6 +709,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, kvmppc_account_exit(vcpu, SYSCALL_EXITS); r = RESUME_GUEST; break; +#endif case BOOKE_INTERRUPT_DTLB_MISS: { unsigned long eaddr = vcpu->arch.fault_dear; @@ -659,12 +850,15 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu) int r; vcpu->arch.pc = 0; - vcpu->arch.shared->msr = 0; - vcpu->arch.shadow_msr = MSR_USER | MSR_DE | MSR_IS | MSR_DS; vcpu->arch.shared->pir = vcpu->vcpu_id; kvmppc_set_gpr(vcpu, 1, (16<<20) - 8); /* -8 for the callee-save LR slot */ + kvmppc_set_msr(vcpu, 0); +#ifndef CONFIG_KVM_BOOKE_HV + vcpu->arch.shadow_msr = MSR_USER | MSR_DE | MSR_IS | MSR_DS; vcpu->arch.shadow_pid = 1; + vcpu->arch.shared->msr = 0; +#endif /* Eye-catching numbers so we know if the guest takes an interrupt * before it's programmed its own IVPR/IVORs. */ @@ -745,8 +939,8 @@ static void get_sregs_base(struct kvm_vcpu *vcpu, sregs->u.e.csrr0 = vcpu->arch.csrr0; sregs->u.e.csrr1 = vcpu->arch.csrr1; sregs->u.e.mcsr = vcpu->arch.mcsr; - sregs->u.e.esr = vcpu->arch.shared->esr; - sregs->u.e.dear = vcpu->arch.shared->dar; + sregs->u.e.esr = get_guest_esr(vcpu); + sregs->u.e.dear = get_guest_dear(vcpu); sregs->u.e.tsr = vcpu->arch.tsr; sregs->u.e.tcr = vcpu->arch.tcr; sregs->u.e.dec = kvmppc_get_dec(vcpu, tb); @@ -763,8 +957,8 @@ static int set_sregs_base(struct kvm_vcpu *vcpu, vcpu->arch.csrr0 = sregs->u.e.csrr0; vcpu->arch.csrr1 = sregs->u.e.csrr1; vcpu->arch.mcsr = sregs->u.e.mcsr; - vcpu->arch.shared->esr = sregs->u.e.esr; - vcpu->arch.shared->dar = sregs->u.e.dear; + set_guest_esr(vcpu, sregs->u.e.esr); + set_guest_dear(vcpu, sregs->u.e.dear); vcpu->arch.vrsave = sregs->u.e.vrsave; kvmppc_set_tcr(vcpu, sregs->u.e.tcr); @@ -961,14 +1155,17 @@ void kvmppc_decrementer_func(unsigned long data) void kvmppc_booke_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { + current->thread.kvm_vcpu = vcpu; } void kvmppc_booke_vcpu_put(struct kvm_vcpu *vcpu) { + current->thread.kvm_vcpu = NULL; } int __init kvmppc_booke_init(void) { +#ifndef CONFIG_KVM_BOOKE_HV unsigned long ivor[16]; unsigned long max_ivor = 0; int i; @@ -1011,7 +1208,7 @@ int __init kvmppc_booke_init(void) } flush_icache_range(kvmppc_booke_handlers, kvmppc_booke_handlers + max_ivor + kvmppc_handler_len); - +#endif /* !BOOKE_HV */ return 0; } diff --git a/arch/powerpc/kvm/booke.h b/arch/powerpc/kvm/booke.h index 05d1d99428ce..d53bcf2558f5 100644 --- a/arch/powerpc/kvm/booke.h +++ b/arch/powerpc/kvm/booke.h @@ -48,7 +48,20 @@ #define BOOKE_IRQPRIO_PERFORMANCE_MONITOR 19 /* Internal pseudo-irqprio for level triggered externals */ #define BOOKE_IRQPRIO_EXTERNAL_LEVEL 20 -#define BOOKE_IRQPRIO_MAX 20 +#define BOOKE_IRQPRIO_DBELL 21 +#define BOOKE_IRQPRIO_DBELL_CRIT 22 +#define BOOKE_IRQPRIO_MAX 23 + +#define BOOKE_IRQMASK_EE ((1 << BOOKE_IRQPRIO_EXTERNAL_LEVEL) | \ + (1 << BOOKE_IRQPRIO_PERFORMANCE_MONITOR) | \ + (1 << BOOKE_IRQPRIO_DBELL) | \ + (1 << BOOKE_IRQPRIO_DECREMENTER) | \ + (1 << BOOKE_IRQPRIO_FIT) | \ + (1 << BOOKE_IRQPRIO_EXTERNAL)) + +#define BOOKE_IRQMASK_CE ((1 << BOOKE_IRQPRIO_DBELL_CRIT) | \ + (1 << BOOKE_IRQPRIO_WATCHDOG) | \ + (1 << BOOKE_IRQPRIO_CRITICAL)) extern unsigned long kvmppc_booke_handlers; @@ -74,4 +87,13 @@ void kvmppc_vcpu_disable_spe(struct kvm_vcpu *vcpu); void kvmppc_booke_vcpu_load(struct kvm_vcpu *vcpu, int cpu); void kvmppc_booke_vcpu_put(struct kvm_vcpu *vcpu); +enum int_class { + INT_CLASS_NONCRIT, + INT_CLASS_CRIT, + INT_CLASS_MC, + INT_CLASS_DBG, +}; + +void kvmppc_set_pending_interrupt(struct kvm_vcpu *vcpu, enum int_class type); + #endif /* __KVM_BOOKE_H__ */ diff --git a/arch/powerpc/kvm/booke_emulate.c b/arch/powerpc/kvm/booke_emulate.c index 3e652da36534..904412bbea40 100644 --- a/arch/powerpc/kvm/booke_emulate.c +++ b/arch/powerpc/kvm/booke_emulate.c @@ -99,6 +99,12 @@ int kvmppc_booke_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu, return emulated; } +/* + * NOTE: some of these registers are not emulated on BOOKE_HV (GS-mode). + * Their backing store is in real registers, and these functions + * will return the wrong result if called for them in another context + * (such as debugging). + */ int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) { int emulated = EMULATE_DONE; @@ -122,9 +128,11 @@ int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) kvmppc_set_tcr(vcpu, spr_val); break; - /* Note: SPRG4-7 are user-readable. These values are - * loaded into the real SPRGs when resuming the - * guest. */ + /* + * Note: SPRG4-7 are user-readable. + * These values are loaded into the real SPRGs when resuming the + * guest (PR-mode only). + */ case SPRN_SPRG4: vcpu->arch.shared->sprg4 = spr_val; break; case SPRN_SPRG5: @@ -136,6 +144,9 @@ int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) case SPRN_IVPR: vcpu->arch.ivpr = spr_val; +#ifdef CONFIG_KVM_BOOKE_HV + mtspr(SPRN_GIVPR, spr_val); +#endif break; case SPRN_IVOR0: vcpu->arch.ivor[BOOKE_IRQPRIO_CRITICAL] = spr_val; @@ -145,6 +156,9 @@ int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) break; case SPRN_IVOR2: vcpu->arch.ivor[BOOKE_IRQPRIO_DATA_STORAGE] = spr_val; +#ifdef CONFIG_KVM_BOOKE_HV + mtspr(SPRN_GIVOR2, spr_val); +#endif break; case SPRN_IVOR3: vcpu->arch.ivor[BOOKE_IRQPRIO_INST_STORAGE] = spr_val; @@ -163,6 +177,9 @@ int kvmppc_booke_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) break; case SPRN_IVOR8: vcpu->arch.ivor[BOOKE_IRQPRIO_SYSCALL] = spr_val; +#ifdef CONFIG_KVM_BOOKE_HV + mtspr(SPRN_GIVOR8, spr_val); +#endif break; case SPRN_IVOR9: vcpu->arch.ivor[BOOKE_IRQPRIO_AP_UNAVAIL] = spr_val; diff --git a/arch/powerpc/kvm/bookehv_interrupts.S b/arch/powerpc/kvm/bookehv_interrupts.S new file mode 100644 index 000000000000..9eaeebd86e44 --- /dev/null +++ b/arch/powerpc/kvm/bookehv_interrupts.S @@ -0,0 +1,587 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + * + * 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. + * + * Author: Varun Sethi + * Author: Scott Wood + * + * This file is derived from arch/powerpc/kvm/booke_interrupts.S + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../kernel/head_booke.h" /* for THREAD_NORMSAVE() */ + +#define GET_VCPU(vcpu, thread) \ + PPC_LL vcpu, THREAD_KVM_VCPU(thread) + +#define SET_VCPU(vcpu) \ + PPC_STL vcpu, (THREAD + THREAD_KVM_VCPU)(r2) + +#define LONGBYTES (BITS_PER_LONG / 8) + +#define VCPU_GPR(n) (VCPU_GPRS + (n * LONGBYTES)) +#define VCPU_GUEST_SPRG(n) (VCPU_GUEST_SPRGS + (n * LONGBYTES)) + +/* The host stack layout: */ +#define HOST_R1 (0 * LONGBYTES) /* Implied by stwu. */ +#define HOST_CALLEE_LR (1 * LONGBYTES) +#define HOST_RUN (2 * LONGBYTES) /* struct kvm_run */ +/* + * r2 is special: it holds 'current', and it made nonvolatile in the + * kernel with the -ffixed-r2 gcc option. + */ +#define HOST_R2 (3 * LONGBYTES) +#define HOST_NV_GPRS (4 * LONGBYTES) +#define HOST_NV_GPR(n) (HOST_NV_GPRS + ((n - 14) * LONGBYTES)) +#define HOST_MIN_STACK_SIZE (HOST_NV_GPR(31) + LONGBYTES) +#define HOST_STACK_SIZE ((HOST_MIN_STACK_SIZE + 15) & ~15) /* Align. */ +#define HOST_STACK_LR (HOST_STACK_SIZE + LONGBYTES) /* In caller stack frame. */ + +#define NEED_EMU 0x00000001 /* emulation -- save nv regs */ +#define NEED_DEAR 0x00000002 /* save faulting DEAR */ +#define NEED_ESR 0x00000004 /* save faulting ESR */ + +/* + * On entry: + * r4 = vcpu, r5 = srr0, r6 = srr1 + * saved in vcpu: cr, ctr, r3-r13 + */ +.macro kvm_handler_common intno, srr0, flags + mfspr r10, SPRN_PID + lwz r8, VCPU_HOST_PID(r4) + PPC_LL r11, VCPU_SHARED(r4) + PPC_STL r14, VCPU_GPR(r14)(r4) /* We need a non-volatile GPR. */ + li r14, \intno + + stw r10, VCPU_GUEST_PID(r4) + mtspr SPRN_PID, r8 + + .if \flags & NEED_EMU + lwz r9, VCPU_KVM(r4) + .endif + +#ifdef CONFIG_KVM_EXIT_TIMING + /* save exit time */ +1: mfspr r7, SPRN_TBRU + mfspr r8, SPRN_TBRL + mfspr r9, SPRN_TBRU + cmpw r9, r7 + PPC_STL r8, VCPU_TIMING_EXIT_TBL(r4) + bne- 1b + PPC_STL r9, VCPU_TIMING_EXIT_TBU(r4) +#endif + + oris r8, r6, MSR_CE@h +#ifndef CONFIG_64BIT + stw r6, (VCPU_SHARED_MSR + 4)(r11) +#else + std r6, (VCPU_SHARED_MSR)(r11) +#endif + ori r8, r8, MSR_ME | MSR_RI + PPC_STL r5, VCPU_PC(r4) + + /* + * Make sure CE/ME/RI are set (if appropriate for exception type) + * whether or not the guest had it set. Since mfmsr/mtmsr are + * somewhat expensive, skip in the common case where the guest + * had all these bits set (and thus they're still set if + * appropriate for the exception type). + */ + cmpw r6, r8 + .if \flags & NEED_EMU + lwz r9, KVM_LPID(r9) + .endif + beq 1f + mfmsr r7 + .if \srr0 != SPRN_MCSRR0 && \srr0 != SPRN_CSRR0 + oris r7, r7, MSR_CE@h + .endif + .if \srr0 != SPRN_MCSRR0 + ori r7, r7, MSR_ME | MSR_RI + .endif + mtmsr r7 +1: + + .if \flags & NEED_EMU + /* + * This assumes you have external PID support. + * To support a bookehv CPU without external PID, you'll + * need to look up the TLB entry and create a temporary mapping. + * + * FIXME: we don't currently handle if the lwepx faults. PR-mode + * booke doesn't handle it either. Since Linux doesn't use + * broadcast tlbivax anymore, the only way this should happen is + * if the guest maps its memory execute-but-not-read, or if we + * somehow take a TLB miss in the middle of this entry code and + * evict the relevant entry. On e500mc, all kernel lowmem is + * bolted into TLB1 large page mappings, and we don't use + * broadcast invalidates, so we should not take a TLB miss here. + * + * Later we'll need to deal with faults here. Disallowing guest + * mappings that are execute-but-not-read could be an option on + * e500mc, but not on chips with an LRAT if it is used. + */ + + mfspr r3, SPRN_EPLC /* will already have correct ELPID and EGS */ + PPC_STL r15, VCPU_GPR(r15)(r4) + PPC_STL r16, VCPU_GPR(r16)(r4) + PPC_STL r17, VCPU_GPR(r17)(r4) + PPC_STL r18, VCPU_GPR(r18)(r4) + PPC_STL r19, VCPU_GPR(r19)(r4) + mr r8, r3 + PPC_STL r20, VCPU_GPR(r20)(r4) + rlwimi r8, r6, EPC_EAS_SHIFT - MSR_IR_LG, EPC_EAS + PPC_STL r21, VCPU_GPR(r21)(r4) + rlwimi r8, r6, EPC_EPR_SHIFT - MSR_PR_LG, EPC_EPR + PPC_STL r22, VCPU_GPR(r22)(r4) + rlwimi r8, r10, EPC_EPID_SHIFT, EPC_EPID + PPC_STL r23, VCPU_GPR(r23)(r4) + PPC_STL r24, VCPU_GPR(r24)(r4) + PPC_STL r25, VCPU_GPR(r25)(r4) + PPC_STL r26, VCPU_GPR(r26)(r4) + PPC_STL r27, VCPU_GPR(r27)(r4) + PPC_STL r28, VCPU_GPR(r28)(r4) + PPC_STL r29, VCPU_GPR(r29)(r4) + PPC_STL r30, VCPU_GPR(r30)(r4) + PPC_STL r31, VCPU_GPR(r31)(r4) + mtspr SPRN_EPLC, r8 + isync + lwepx r9, 0, r5 + mtspr SPRN_EPLC, r3 + stw r9, VCPU_LAST_INST(r4) + .endif + + .if \flags & NEED_ESR + mfspr r8, SPRN_ESR + PPC_STL r8, VCPU_FAULT_ESR(r4) + .endif + + .if \flags & NEED_DEAR + mfspr r9, SPRN_DEAR + PPC_STL r9, VCPU_FAULT_DEAR(r4) + .endif + + b kvmppc_resume_host +.endm + +/* + * For input register values, see arch/powerpc/include/asm/kvm_booke_hv_asm.h + */ +.macro kvm_handler intno srr0, srr1, flags +_GLOBAL(kvmppc_handler_\intno\()_\srr1) + GET_VCPU(r11, r10) + PPC_STL r3, VCPU_GPR(r3)(r11) + mfspr r3, SPRN_SPRG_RSCRATCH0 + PPC_STL r4, VCPU_GPR(r4)(r11) + PPC_LL r4, THREAD_NORMSAVE(0)(r10) + PPC_STL r5, VCPU_GPR(r5)(r11) + PPC_STL r13, VCPU_CR(r11) + mfspr r5, \srr0 + PPC_STL r3, VCPU_GPR(r10)(r11) + PPC_LL r3, THREAD_NORMSAVE(2)(r10) + PPC_STL r6, VCPU_GPR(r6)(r11) + PPC_STL r4, VCPU_GPR(r11)(r11) + mfspr r6, \srr1 + PPC_STL r7, VCPU_GPR(r7)(r11) + PPC_STL r8, VCPU_GPR(r8)(r11) + PPC_STL r9, VCPU_GPR(r9)(r11) + PPC_STL r3, VCPU_GPR(r13)(r11) + mfctr r7 + PPC_STL r12, VCPU_GPR(r12)(r11) + PPC_STL r7, VCPU_CTR(r11) + mr r4, r11 + kvm_handler_common \intno, \srr0, \flags +.endm + +.macro kvm_lvl_handler intno scratch srr0, srr1, flags +_GLOBAL(kvmppc_handler_\intno\()_\srr1) + mfspr r10, SPRN_SPRG_THREAD + GET_VCPU(r11, r10) + PPC_STL r3, VCPU_GPR(r3)(r11) + mfspr r3, \scratch + PPC_STL r4, VCPU_GPR(r4)(r11) + PPC_LL r4, GPR9(r8) + PPC_STL r5, VCPU_GPR(r5)(r11) + PPC_STL r9, VCPU_CR(r11) + mfspr r5, \srr0 + PPC_STL r3, VCPU_GPR(r8)(r11) + PPC_LL r3, GPR10(r8) + PPC_STL r6, VCPU_GPR(r6)(r11) + PPC_STL r4, VCPU_GPR(r9)(r11) + mfspr r6, \srr1 + PPC_LL r4, GPR11(r8) + PPC_STL r7, VCPU_GPR(r7)(r11) + PPC_STL r8, VCPU_GPR(r8)(r11) + PPC_STL r3, VCPU_GPR(r10)(r11) + mfctr r7 + PPC_STL r12, VCPU_GPR(r12)(r11) + PPC_STL r4, VCPU_GPR(r11)(r11) + PPC_STL r7, VCPU_CTR(r11) + mr r4, r11 + kvm_handler_common \intno, \srr0, \flags +.endm + +kvm_lvl_handler BOOKE_INTERRUPT_CRITICAL, \ + SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0 +kvm_lvl_handler BOOKE_INTERRUPT_MACHINE_CHECK, \ + SPRN_SPRG_RSCRATCH_MC, SPRN_MCSRR0, SPRN_MCSRR1, 0 +kvm_handler BOOKE_INTERRUPT_DATA_STORAGE, \ + SPRN_SRR0, SPRN_SRR1, (NEED_EMU | NEED_DEAR) +kvm_handler BOOKE_INTERRUPT_INST_STORAGE, SPRN_SRR0, SPRN_SRR1, NEED_ESR +kvm_handler BOOKE_INTERRUPT_EXTERNAL, SPRN_SRR0, SPRN_SRR1, 0 +kvm_handler BOOKE_INTERRUPT_ALIGNMENT, \ + SPRN_SRR0, SPRN_SRR1, (NEED_DEAR | NEED_ESR) +kvm_handler BOOKE_INTERRUPT_PROGRAM, SPRN_SRR0, SPRN_SRR1, NEED_ESR +kvm_handler BOOKE_INTERRUPT_FP_UNAVAIL, SPRN_SRR0, SPRN_SRR1, 0 +kvm_handler BOOKE_INTERRUPT_SYSCALL, SPRN_SRR0, SPRN_SRR1, 0 +kvm_handler BOOKE_INTERRUPT_AP_UNAVAIL, SPRN_SRR0, SPRN_SRR1, 0 +kvm_handler BOOKE_INTERRUPT_DECREMENTER, SPRN_SRR0, SPRN_SRR1, 0 +kvm_handler BOOKE_INTERRUPT_FIT, SPRN_SRR0, SPRN_SRR1, 0 +kvm_lvl_handler BOOKE_INTERRUPT_WATCHDOG, \ + SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0 +kvm_handler BOOKE_INTERRUPT_DTLB_MISS, \ + SPRN_SRR0, SPRN_SRR1, (NEED_EMU | NEED_DEAR | NEED_ESR) +kvm_handler BOOKE_INTERRUPT_ITLB_MISS, SPRN_SRR0, SPRN_SRR1, 0 +kvm_handler BOOKE_INTERRUPT_SPE_UNAVAIL, SPRN_SRR0, SPRN_SRR1, 0 +kvm_handler BOOKE_INTERRUPT_SPE_FP_DATA, SPRN_SRR0, SPRN_SRR1, 0 +kvm_handler BOOKE_INTERRUPT_SPE_FP_ROUND, SPRN_SRR0, SPRN_SRR1, 0 +kvm_handler BOOKE_INTERRUPT_PERFORMANCE_MONITOR, SPRN_SRR0, SPRN_SRR1, 0 +kvm_handler BOOKE_INTERRUPT_DOORBELL, SPRN_SRR0, SPRN_SRR1, 0 +kvm_lvl_handler BOOKE_INTERRUPT_DOORBELL_CRITICAL, \ + SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0 +kvm_handler BOOKE_INTERRUPT_HV_PRIV, SPRN_SRR0, SPRN_SRR1, NEED_EMU +kvm_handler BOOKE_INTERRUPT_HV_SYSCALL, SPRN_SRR0, SPRN_SRR1, 0 +kvm_handler BOOKE_INTERRUPT_GUEST_DBELL, SPRN_GSRR0, SPRN_GSRR1, 0 +kvm_lvl_handler BOOKE_INTERRUPT_GUEST_DBELL_CRIT, \ + SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0 +kvm_lvl_handler BOOKE_INTERRUPT_DEBUG, \ + SPRN_SPRG_RSCRATCH_CRIT, SPRN_CSRR0, SPRN_CSRR1, 0 +kvm_lvl_handler BOOKE_INTERRUPT_DEBUG, \ + SPRN_SPRG_RSCRATCH_DBG, SPRN_DSRR0, SPRN_DSRR1, 0 + + +/* Registers: + * SPRG_SCRATCH0: guest r10 + * r4: vcpu pointer + * r11: vcpu->arch.shared + * r14: KVM exit number + */ +_GLOBAL(kvmppc_resume_host) + /* Save remaining volatile guest register state to vcpu. */ + mfspr r3, SPRN_VRSAVE + PPC_STL r0, VCPU_GPR(r0)(r4) + PPC_STL r1, VCPU_GPR(r1)(r4) + mflr r5 + mfspr r6, SPRN_SPRG4 + PPC_STL r2, VCPU_GPR(r2)(r4) + PPC_STL r5, VCPU_LR(r4) + mfspr r7, SPRN_SPRG5 + PPC_STL r3, VCPU_VRSAVE(r4) + PPC_STL r6, VCPU_SHARED_SPRG4(r11) + mfspr r8, SPRN_SPRG6 + PPC_STL r7, VCPU_SHARED_SPRG5(r11) + mfspr r9, SPRN_SPRG7 + PPC_STL r8, VCPU_SHARED_SPRG6(r11) + mfxer r3 + PPC_STL r9, VCPU_SHARED_SPRG7(r11) + + /* save guest MAS registers and restore host mas4 & mas6 */ + mfspr r5, SPRN_MAS0 + PPC_STL r3, VCPU_XER(r4) + mfspr r6, SPRN_MAS1 + stw r5, VCPU_SHARED_MAS0(r11) + mfspr r7, SPRN_MAS2 + stw r6, VCPU_SHARED_MAS1(r11) +#ifndef CONFIG_64BIT + stw r7, (VCPU_SHARED_MAS2 + 4)(r11) +#else + std r7, (VCPU_SHARED_MAS2)(r11) +#endif + mfspr r5, SPRN_MAS3 + mfspr r6, SPRN_MAS4 + stw r5, VCPU_SHARED_MAS7_3+4(r11) + mfspr r7, SPRN_MAS6 + stw r6, VCPU_SHARED_MAS4(r11) + mfspr r5, SPRN_MAS7 + lwz r6, VCPU_HOST_MAS4(r4) + stw r7, VCPU_SHARED_MAS6(r11) + lwz r8, VCPU_HOST_MAS6(r4) + mtspr SPRN_MAS4, r6 + stw r5, VCPU_SHARED_MAS7_3+0(r11) + mtspr SPRN_MAS6, r8 + mfspr r3, SPRN_EPCR + rlwinm r3, r3, 0, ~SPRN_EPCR_DMIUH + mtspr SPRN_EPCR, r3 + isync + + /* Restore host stack pointer */ + PPC_LL r1, VCPU_HOST_STACK(r4) + PPC_LL r2, HOST_R2(r1) + + /* Switch to kernel stack and jump to handler. */ + PPC_LL r3, HOST_RUN(r1) + mr r5, r14 /* intno */ + mr r14, r4 /* Save vcpu pointer. */ + bl kvmppc_handle_exit + + /* Restore vcpu pointer and the nonvolatiles we used. */ + mr r4, r14 + PPC_LL r14, VCPU_GPR(r14)(r4) + + andi. r5, r3, RESUME_FLAG_NV + beq skip_nv_load + PPC_LL r15, VCPU_GPR(r15)(r4) + PPC_LL r16, VCPU_GPR(r16)(r4) + PPC_LL r17, VCPU_GPR(r17)(r4) + PPC_LL r18, VCPU_GPR(r18)(r4) + PPC_LL r19, VCPU_GPR(r19)(r4) + PPC_LL r20, VCPU_GPR(r20)(r4) + PPC_LL r21, VCPU_GPR(r21)(r4) + PPC_LL r22, VCPU_GPR(r22)(r4) + PPC_LL r23, VCPU_GPR(r23)(r4) + PPC_LL r24, VCPU_GPR(r24)(r4) + PPC_LL r25, VCPU_GPR(r25)(r4) + PPC_LL r26, VCPU_GPR(r26)(r4) + PPC_LL r27, VCPU_GPR(r27)(r4) + PPC_LL r28, VCPU_GPR(r28)(r4) + PPC_LL r29, VCPU_GPR(r29)(r4) + PPC_LL r30, VCPU_GPR(r30)(r4) + PPC_LL r31, VCPU_GPR(r31)(r4) +skip_nv_load: + /* Should we return to the guest? */ + andi. r5, r3, RESUME_FLAG_HOST + beq lightweight_exit + + srawi r3, r3, 2 /* Shift -ERR back down. */ + +heavyweight_exit: + /* Not returning to guest. */ + PPC_LL r5, HOST_STACK_LR(r1) + + /* + * We already saved guest volatile register state; now save the + * non-volatiles. + */ + + PPC_STL r15, VCPU_GPR(r15)(r4) + PPC_STL r16, VCPU_GPR(r16)(r4) + PPC_STL r17, VCPU_GPR(r17)(r4) + PPC_STL r18, VCPU_GPR(r18)(r4) + PPC_STL r19, VCPU_GPR(r19)(r4) + PPC_STL r20, VCPU_GPR(r20)(r4) + PPC_STL r21, VCPU_GPR(r21)(r4) + PPC_STL r22, VCPU_GPR(r22)(r4) + PPC_STL r23, VCPU_GPR(r23)(r4) + PPC_STL r24, VCPU_GPR(r24)(r4) + PPC_STL r25, VCPU_GPR(r25)(r4) + PPC_STL r26, VCPU_GPR(r26)(r4) + PPC_STL r27, VCPU_GPR(r27)(r4) + PPC_STL r28, VCPU_GPR(r28)(r4) + PPC_STL r29, VCPU_GPR(r29)(r4) + PPC_STL r30, VCPU_GPR(r30)(r4) + PPC_STL r31, VCPU_GPR(r31)(r4) + + /* Load host non-volatile register state from host stack. */ + PPC_LL r14, HOST_NV_GPR(r14)(r1) + PPC_LL r15, HOST_NV_GPR(r15)(r1) + PPC_LL r16, HOST_NV_GPR(r16)(r1) + PPC_LL r17, HOST_NV_GPR(r17)(r1) + PPC_LL r18, HOST_NV_GPR(r18)(r1) + PPC_LL r19, HOST_NV_GPR(r19)(r1) + PPC_LL r20, HOST_NV_GPR(r20)(r1) + PPC_LL r21, HOST_NV_GPR(r21)(r1) + PPC_LL r22, HOST_NV_GPR(r22)(r1) + PPC_LL r23, HOST_NV_GPR(r23)(r1) + PPC_LL r24, HOST_NV_GPR(r24)(r1) + PPC_LL r25, HOST_NV_GPR(r25)(r1) + PPC_LL r26, HOST_NV_GPR(r26)(r1) + PPC_LL r27, HOST_NV_GPR(r27)(r1) + PPC_LL r28, HOST_NV_GPR(r28)(r1) + PPC_LL r29, HOST_NV_GPR(r29)(r1) + PPC_LL r30, HOST_NV_GPR(r30)(r1) + PPC_LL r31, HOST_NV_GPR(r31)(r1) + + /* Return to kvm_vcpu_run(). */ + mtlr r5 + addi r1, r1, HOST_STACK_SIZE + /* r3 still contains the return code from kvmppc_handle_exit(). */ + blr + +/* Registers: + * r3: kvm_run pointer + * r4: vcpu pointer + */ +_GLOBAL(__kvmppc_vcpu_run) + stwu r1, -HOST_STACK_SIZE(r1) + PPC_STL r1, VCPU_HOST_STACK(r4) /* Save stack pointer to vcpu. */ + + /* Save host state to stack. */ + PPC_STL r3, HOST_RUN(r1) + mflr r3 + PPC_STL r3, HOST_STACK_LR(r1) + + /* Save host non-volatile register state to stack. */ + PPC_STL r14, HOST_NV_GPR(r14)(r1) + PPC_STL r15, HOST_NV_GPR(r15)(r1) + PPC_STL r16, HOST_NV_GPR(r16)(r1) + PPC_STL r17, HOST_NV_GPR(r17)(r1) + PPC_STL r18, HOST_NV_GPR(r18)(r1) + PPC_STL r19, HOST_NV_GPR(r19)(r1) + PPC_STL r20, HOST_NV_GPR(r20)(r1) + PPC_STL r21, HOST_NV_GPR(r21)(r1) + PPC_STL r22, HOST_NV_GPR(r22)(r1) + PPC_STL r23, HOST_NV_GPR(r23)(r1) + PPC_STL r24, HOST_NV_GPR(r24)(r1) + PPC_STL r25, HOST_NV_GPR(r25)(r1) + PPC_STL r26, HOST_NV_GPR(r26)(r1) + PPC_STL r27, HOST_NV_GPR(r27)(r1) + PPC_STL r28, HOST_NV_GPR(r28)(r1) + PPC_STL r29, HOST_NV_GPR(r29)(r1) + PPC_STL r30, HOST_NV_GPR(r30)(r1) + PPC_STL r31, HOST_NV_GPR(r31)(r1) + + /* Load guest non-volatiles. */ + PPC_LL r14, VCPU_GPR(r14)(r4) + PPC_LL r15, VCPU_GPR(r15)(r4) + PPC_LL r16, VCPU_GPR(r16)(r4) + PPC_LL r17, VCPU_GPR(r17)(r4) + PPC_LL r18, VCPU_GPR(r18)(r4) + PPC_LL r19, VCPU_GPR(r19)(r4) + PPC_LL r20, VCPU_GPR(r20)(r4) + PPC_LL r21, VCPU_GPR(r21)(r4) + PPC_LL r22, VCPU_GPR(r22)(r4) + PPC_LL r23, VCPU_GPR(r23)(r4) + PPC_LL r24, VCPU_GPR(r24)(r4) + PPC_LL r25, VCPU_GPR(r25)(r4) + PPC_LL r26, VCPU_GPR(r26)(r4) + PPC_LL r27, VCPU_GPR(r27)(r4) + PPC_LL r28, VCPU_GPR(r28)(r4) + PPC_LL r29, VCPU_GPR(r29)(r4) + PPC_LL r30, VCPU_GPR(r30)(r4) + PPC_LL r31, VCPU_GPR(r31)(r4) + + +lightweight_exit: + PPC_STL r2, HOST_R2(r1) + + mfspr r3, SPRN_PID + stw r3, VCPU_HOST_PID(r4) + lwz r3, VCPU_GUEST_PID(r4) + mtspr SPRN_PID, r3 + + /* Save vcpu pointer for the exception handlers + * must be done before loading guest r2. + */ +// SET_VCPU(r4) + + PPC_LL r11, VCPU_SHARED(r4) + /* Save host mas4 and mas6 and load guest MAS registers */ + mfspr r3, SPRN_MAS4 + stw r3, VCPU_HOST_MAS4(r4) + mfspr r3, SPRN_MAS6 + stw r3, VCPU_HOST_MAS6(r4) + lwz r3, VCPU_SHARED_MAS0(r11) + lwz r5, VCPU_SHARED_MAS1(r11) +#ifndef CONFIG_64BIT + lwz r6, (VCPU_SHARED_MAS2 + 4)(r11) +#else + ld r6, (VCPU_SHARED_MAS2)(r11) +#endif + lwz r7, VCPU_SHARED_MAS7_3+4(r11) + lwz r8, VCPU_SHARED_MAS4(r11) + mtspr SPRN_MAS0, r3 + mtspr SPRN_MAS1, r5 + mtspr SPRN_MAS2, r6 + mtspr SPRN_MAS3, r7 + mtspr SPRN_MAS4, r8 + lwz r3, VCPU_SHARED_MAS6(r11) + lwz r5, VCPU_SHARED_MAS7_3+0(r11) + mtspr SPRN_MAS6, r3 + mtspr SPRN_MAS7, r5 + /* Disable MAS register updates via exception */ + mfspr r3, SPRN_EPCR + oris r3, r3, SPRN_EPCR_DMIUH@h + mtspr SPRN_EPCR, r3 + + /* + * Host interrupt handlers may have clobbered these guest-readable + * SPRGs, so we need to reload them here with the guest's values. + */ + lwz r3, VCPU_VRSAVE(r4) + lwz r5, VCPU_SHARED_SPRG4(r11) + mtspr SPRN_VRSAVE, r3 + lwz r6, VCPU_SHARED_SPRG5(r11) + mtspr SPRN_SPRG4W, r5 + lwz r7, VCPU_SHARED_SPRG6(r11) + mtspr SPRN_SPRG5W, r6 + lwz r8, VCPU_SHARED_SPRG7(r11) + mtspr SPRN_SPRG6W, r7 + mtspr SPRN_SPRG7W, r8 + + /* Load some guest volatiles. */ + PPC_LL r3, VCPU_LR(r4) + PPC_LL r5, VCPU_XER(r4) + PPC_LL r6, VCPU_CTR(r4) + PPC_LL r7, VCPU_CR(r4) + PPC_LL r8, VCPU_PC(r4) +#ifndef CONFIG_64BIT + lwz r9, (VCPU_SHARED_MSR + 4)(r11) +#else + ld r9, (VCPU_SHARED_MSR)(r11) +#endif + PPC_LL r0, VCPU_GPR(r0)(r4) + PPC_LL r1, VCPU_GPR(r1)(r4) + PPC_LL r2, VCPU_GPR(r2)(r4) + PPC_LL r10, VCPU_GPR(r10)(r4) + PPC_LL r11, VCPU_GPR(r11)(r4) + PPC_LL r12, VCPU_GPR(r12)(r4) + PPC_LL r13, VCPU_GPR(r13)(r4) + mtlr r3 + mtxer r5 + mtctr r6 + mtcr r7 + mtsrr0 r8 + mtsrr1 r9 + +#ifdef CONFIG_KVM_EXIT_TIMING + /* save enter time */ +1: + mfspr r6, SPRN_TBRU + mfspr r7, SPRN_TBRL + mfspr r8, SPRN_TBRU + cmpw r8, r6 + PPC_STL r7, VCPU_TIMING_LAST_ENTER_TBL(r4) + bne 1b + PPC_STL r8, VCPU_TIMING_LAST_ENTER_TBU(r4) +#endif + + /* Finish loading guest volatiles and jump to guest. */ + PPC_LL r5, VCPU_GPR(r5)(r4) + PPC_LL r6, VCPU_GPR(r6)(r4) + PPC_LL r7, VCPU_GPR(r7)(r4) + PPC_LL r8, VCPU_GPR(r8)(r4) + PPC_LL r9, VCPU_GPR(r9)(r4) + + PPC_LL r3, VCPU_GPR(r3)(r4) + PPC_LL r4, VCPU_GPR(r4)(r4) + rfi diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index cd53e08403b3..6a530e4b3e7c 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -114,6 +114,11 @@ int kvmppc_sanity_check(struct kvm_vcpu *vcpu) goto out; #endif +#ifdef CONFIG_KVM_BOOKE_HV + if (!cpu_has_feature(CPU_FTR_EMB_HV)) + goto out; +#endif + r = true; out: diff --git a/arch/powerpc/kvm/timing.h b/arch/powerpc/kvm/timing.h index 8167d42a776f..bf191e72b2d8 100644 --- a/arch/powerpc/kvm/timing.h +++ b/arch/powerpc/kvm/timing.h @@ -93,6 +93,12 @@ static inline void kvmppc_account_exit_stat(struct kvm_vcpu *vcpu, int type) case SIGNAL_EXITS: vcpu->stat.signal_exits++; break; + case DBELL_EXITS: + vcpu->stat.dbell_exits++; + break; + case GDBELL_EXITS: + vcpu->stat.gdbell_exits++; + break; } } -- cgit v1.2.3 From 8fae845f4956de0becc115e926d33eff46722e94 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:45 +0000 Subject: KVM: PPC: booke: standard PPC floating point support e500mc has a normal PPC FPU, rather than SPE which is found on e500v1/v2. Based on code from Liu Yu . Signed-off-by: Scott Wood Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/switch_to.h | 1 + arch/powerpc/kvm/booke.c | 44 ++++++++++++++++++++++++++++++++++++ arch/powerpc/kvm/booke.h | 31 +++++++++++++++++++++++++ 3 files changed, 76 insertions(+) (limited to 'arch') diff --git a/arch/powerpc/include/asm/switch_to.h b/arch/powerpc/include/asm/switch_to.h index caf82d0a00de..1622c356ba90 100644 --- a/arch/powerpc/include/asm/switch_to.h +++ b/arch/powerpc/include/asm/switch_to.h @@ -17,6 +17,7 @@ extern struct task_struct *_switch(struct thread_struct *prev, struct thread_struct *next); extern void giveup_fpu(struct task_struct *); +extern void load_up_fpu(void); extern void disable_kernel_fp(void); extern void enable_kernel_fp(void); extern void flush_fp_to_thread(struct task_struct *); diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 75dbaeb2efa3..0b77be187cf7 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -457,6 +457,11 @@ void kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu) int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) { int ret; +#ifdef CONFIG_PPC_FPU + unsigned int fpscr; + int fpexc_mode; + u64 fpr[32]; +#endif if (!vcpu->arch.sane) { kvm_run->exit_reason = KVM_EXIT_INTERNAL_ERROR; @@ -479,7 +484,46 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) } kvm_guest_enter(); + +#ifdef CONFIG_PPC_FPU + /* Save userspace FPU state in stack */ + enable_kernel_fp(); + memcpy(fpr, current->thread.fpr, sizeof(current->thread.fpr)); + fpscr = current->thread.fpscr.val; + fpexc_mode = current->thread.fpexc_mode; + + /* Restore guest FPU state to thread */ + memcpy(current->thread.fpr, vcpu->arch.fpr, sizeof(vcpu->arch.fpr)); + current->thread.fpscr.val = vcpu->arch.fpscr; + + /* + * Since we can't trap on MSR_FP in GS-mode, we consider the guest + * as always using the FPU. Kernel usage of FP (via + * enable_kernel_fp()) in this thread must not occur while + * vcpu->fpu_active is set. + */ + vcpu->fpu_active = 1; + + kvmppc_load_guest_fp(vcpu); +#endif + ret = __kvmppc_vcpu_run(kvm_run, vcpu); + +#ifdef CONFIG_PPC_FPU + kvmppc_save_guest_fp(vcpu); + + vcpu->fpu_active = 0; + + /* Save guest FPU state from thread */ + memcpy(vcpu->arch.fpr, current->thread.fpr, sizeof(vcpu->arch.fpr)); + vcpu->arch.fpscr = current->thread.fpscr.val; + + /* Restore userspace FPU state from stack */ + memcpy(current->thread.fpr, fpr, sizeof(current->thread.fpr)); + current->thread.fpscr.val = fpscr; + current->thread.fpexc_mode = fpexc_mode; +#endif + kvm_guest_exit(); out: diff --git a/arch/powerpc/kvm/booke.h b/arch/powerpc/kvm/booke.h index d53bcf2558f5..62c4fe55d19b 100644 --- a/arch/powerpc/kvm/booke.h +++ b/arch/powerpc/kvm/booke.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "timing.h" /* interrupt priortity ordering */ @@ -96,4 +97,34 @@ enum int_class { void kvmppc_set_pending_interrupt(struct kvm_vcpu *vcpu, enum int_class type); +/* + * Load up guest vcpu FP state if it's needed. + * It also set the MSR_FP in thread so that host know + * we're holding FPU, and then host can help to save + * guest vcpu FP state if other threads require to use FPU. + * This simulates an FP unavailable fault. + * + * It requires to be called with preemption disabled. + */ +static inline void kvmppc_load_guest_fp(struct kvm_vcpu *vcpu) +{ +#ifdef CONFIG_PPC_FPU + if (vcpu->fpu_active && !(current->thread.regs->msr & MSR_FP)) { + load_up_fpu(); + current->thread.regs->msr |= MSR_FP; + } +#endif +} + +/* + * Save guest vcpu FP state into thread. + * It requires to be called with preemption disabled. + */ +static inline void kvmppc_save_guest_fp(struct kvm_vcpu *vcpu) +{ +#ifdef CONFIG_PPC_FPU + if (vcpu->fpu_active && (current->thread.regs->msr & MSR_FP)) + giveup_fpu(current); +#endif +} #endif /* __KVM_BOOKE_H__ */ -- cgit v1.2.3 From 73196cd364a2d972d73fa08da9d81ca3215bed68 Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Tue, 20 Dec 2011 15:34:47 +0000 Subject: KVM: PPC: e500mc support Add processor support for e500mc, using hardware virtualization support (GS-mode). Current issues include: - No support for external proxy (coreint) interrupt mode in the guest. Includes work by Ashish Kalra , Varun Sethi , and Liu Yu . Signed-off-by: Scott Wood Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/cputable.h | 6 +- arch/powerpc/include/asm/kvm.h | 1 + arch/powerpc/kernel/cpu_setup_fsl_booke.S | 1 + arch/powerpc/kernel/head_fsl_booke.S | 46 ++++ arch/powerpc/kvm/Kconfig | 17 +- arch/powerpc/kvm/Makefile | 11 + arch/powerpc/kvm/e500.h | 13 +- arch/powerpc/kvm/e500_emulate.c | 24 ++- arch/powerpc/kvm/e500_tlb.c | 21 +- arch/powerpc/kvm/e500mc.c | 342 ++++++++++++++++++++++++++++++ arch/powerpc/kvm/powerpc.c | 6 +- 11 files changed, 476 insertions(+), 12 deletions(-) create mode 100644 arch/powerpc/kvm/e500mc.c (limited to 'arch') diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index 7108a9c490a3..67c34afc6b5d 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -168,6 +168,7 @@ extern const char *powerpc_base_platform; #define CPU_FTR_LWSYNC ASM_CONST(0x0000000008000000) #define CPU_FTR_NOEXECUTE ASM_CONST(0x0000000010000000) #define CPU_FTR_INDEXED_DCR ASM_CONST(0x0000000020000000) +#define CPU_FTR_EMB_HV ASM_CONST(0x0000000040000000) /* * Add the 64-bit processor unique features in the top half of the word; @@ -386,11 +387,11 @@ extern const char *powerpc_base_platform; CPU_FTR_NODSISRALIGN | CPU_FTR_NOEXECUTE) #define CPU_FTRS_E500MC (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | \ CPU_FTR_L2CSR | CPU_FTR_LWSYNC | CPU_FTR_NOEXECUTE | \ - CPU_FTR_DBELL | CPU_FTR_DEBUG_LVL_EXC) + CPU_FTR_DBELL | CPU_FTR_DEBUG_LVL_EXC | CPU_FTR_EMB_HV) #define CPU_FTRS_E5500 (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | \ CPU_FTR_L2CSR | CPU_FTR_LWSYNC | CPU_FTR_NOEXECUTE | \ CPU_FTR_DBELL | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ - CPU_FTR_DEBUG_LVL_EXC) + CPU_FTR_DEBUG_LVL_EXC | CPU_FTR_EMB_HV) #define CPU_FTRS_E6500 (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | \ CPU_FTR_L2CSR | CPU_FTR_LWSYNC | CPU_FTR_NOEXECUTE | \ CPU_FTR_DBELL | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ @@ -539,6 +540,7 @@ enum { #ifdef CONFIG_PPC_E500MC CPU_FTRS_E500MC & CPU_FTRS_E5500 & CPU_FTRS_E6500 & #endif + ~CPU_FTR_EMB_HV & /* can be removed at runtime */ CPU_FTRS_POSSIBLE, }; #endif /* __powerpc64__ */ diff --git a/arch/powerpc/include/asm/kvm.h b/arch/powerpc/include/asm/kvm.h index b921c3f48928..1bea4d8ea6f4 100644 --- a/arch/powerpc/include/asm/kvm.h +++ b/arch/powerpc/include/asm/kvm.h @@ -277,6 +277,7 @@ struct kvm_sync_regs { #define KVM_CPU_E500V2 2 #define KVM_CPU_3S_32 3 #define KVM_CPU_3S_64 4 +#define KVM_CPU_E500MC 5 /* for KVM_CAP_SPAPR_TCE */ struct kvm_create_spapr_tce { diff --git a/arch/powerpc/kernel/cpu_setup_fsl_booke.S b/arch/powerpc/kernel/cpu_setup_fsl_booke.S index 8053db02b85e..69fdd2322a66 100644 --- a/arch/powerpc/kernel/cpu_setup_fsl_booke.S +++ b/arch/powerpc/kernel/cpu_setup_fsl_booke.S @@ -73,6 +73,7 @@ _GLOBAL(__setup_cpu_e500v2) mtlr r4 blr _GLOBAL(__setup_cpu_e500mc) + mr r5, r4 mflr r4 bl __e500_icache_setup bl __e500_dcache_setup diff --git a/arch/powerpc/kernel/head_fsl_booke.S b/arch/powerpc/kernel/head_fsl_booke.S index 7c406dd9fea6..89c6d6f36785 100644 --- a/arch/powerpc/kernel/head_fsl_booke.S +++ b/arch/powerpc/kernel/head_fsl_booke.S @@ -380,10 +380,16 @@ interrupt_base: mtspr SPRN_SPRG_WSCRATCH0, r10 /* Save some working registers */ mfspr r10, SPRN_SPRG_THREAD stw r11, THREAD_NORMSAVE(0)(r10) +#ifdef CONFIG_KVM_BOOKE_HV +BEGIN_FTR_SECTION + mfspr r11, SPRN_SRR1 +END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV) +#endif stw r12, THREAD_NORMSAVE(1)(r10) stw r13, THREAD_NORMSAVE(2)(r10) mfcr r13 stw r13, THREAD_NORMSAVE(3)(r10) + DO_KVM BOOKE_INTERRUPT_DTLB_MISS SPRN_SRR1 mfspr r10, SPRN_DEAR /* Get faulting address */ /* If we are faulting a kernel address, we have to use the @@ -468,10 +474,16 @@ interrupt_base: mtspr SPRN_SPRG_WSCRATCH0, r10 /* Save some working registers */ mfspr r10, SPRN_SPRG_THREAD stw r11, THREAD_NORMSAVE(0)(r10) +#ifdef CONFIG_KVM_BOOKE_HV +BEGIN_FTR_SECTION + mfspr r11, SPRN_SRR1 +END_FTR_SECTION_IFSET(CPU_FTR_EMB_HV) +#endif stw r12, THREAD_NORMSAVE(1)(r10) stw r13, THREAD_NORMSAVE(2)(r10) mfcr r13 stw r13, THREAD_NORMSAVE(3)(r10) + DO_KVM BOOKE_INTERRUPT_ITLB_MISS SPRN_SRR1 mfspr r10, SPRN_SRR0 /* Get faulting address */ /* If we are faulting a kernel address, we have to use the @@ -580,6 +592,17 @@ interrupt_base: DEBUG_DEBUG_EXCEPTION DEBUG_CRIT_EXCEPTION + GUEST_DOORBELL_EXCEPTION + + CRITICAL_EXCEPTION(0, GUEST_DBELL_CRIT, CriticalGuestDoorbell, \ + unknown_exception) + + /* Hypercall */ + EXCEPTION(0, HV_SYSCALL, Hypercall, unknown_exception, EXC_XFER_EE) + + /* Embedded Hypervisor Privilege */ + EXCEPTION(0, HV_PRIV, Ehvpriv, unknown_exception, EXC_XFER_EE) + /* * Local functions */ @@ -883,8 +906,31 @@ _GLOBAL(__setup_e500mc_ivors) mtspr SPRN_IVOR36,r3 li r3,CriticalDoorbell@l mtspr SPRN_IVOR37,r3 + + /* + * We only want to touch IVOR38-41 if we're running on hardware + * that supports category E.HV. The architectural way to determine + * this is MMUCFG[LPIDSIZE]. + */ + mfspr r3, SPRN_MMUCFG + andis. r3, r3, MMUCFG_LPIDSIZE@h + beq no_hv + li r3,GuestDoorbell@l + mtspr SPRN_IVOR38,r3 + li r3,CriticalGuestDoorbell@l + mtspr SPRN_IVOR39,r3 + li r3,Hypercall@l + mtspr SPRN_IVOR40,r3 + li r3,Ehvpriv@l + mtspr SPRN_IVOR41,r3 +skip_hv_ivors: sync blr +no_hv: + lwz r3, CPU_SPEC_FEATURES(r5) + rlwinm r3, r3, 0, ~CPU_FTR_EMB_HV + stw r3, CPU_SPEC_FEATURES(r5) + b skip_hv_ivors /* * extern void giveup_altivec(struct task_struct *prev) diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig index 2c33cd336434..58f6e6817924 100644 --- a/arch/powerpc/kvm/Kconfig +++ b/arch/powerpc/kvm/Kconfig @@ -109,7 +109,7 @@ config KVM_440 config KVM_EXIT_TIMING bool "Detailed exit timing" - depends on KVM_440 || KVM_E500 + depends on KVM_440 || KVM_E500 || KVM_E500MC ---help--- Calculate elapsed time for every exit/enter cycle. A per-vcpu report is available in debugfs kvm/vm#_vcpu#_timing. @@ -132,6 +132,21 @@ config KVM_E500 If unsure, say N. +config KVM_E500MC + bool "KVM support for PowerPC E500MC/E5500 processors" + depends on EXPERIMENTAL && PPC_E500MC + select KVM + select KVM_MMIO + select KVM_BOOKE_HV + ---help--- + Support running unmodified E500MC/E5500 (32-bit) guest kernels in + virtual machines on E500MC/E5500 host processors. + + This module provides access to the hardware capabilities through + a character device node named /dev/kvm. + + If unsure, say N. + source drivers/vhost/Kconfig endif # VIRTUALIZATION diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile index 3688aeecc4b2..62febd730ba4 100644 --- a/arch/powerpc/kvm/Makefile +++ b/arch/powerpc/kvm/Makefile @@ -38,6 +38,16 @@ kvm-e500-objs := \ e500_emulate.o kvm-objs-$(CONFIG_KVM_E500) := $(kvm-e500-objs) +kvm-e500mc-objs := \ + $(common-objs-y) \ + booke.o \ + booke_emulate.o \ + bookehv_interrupts.o \ + e500mc.o \ + e500_tlb.o \ + e500_emulate.o +kvm-objs-$(CONFIG_KVM_E500MC) := $(kvm-e500mc-objs) + kvm-book3s_64-objs-$(CONFIG_KVM_BOOK3S_64_PR) := \ ../../../virt/kvm/coalesced_mmio.o \ fpu.o \ @@ -89,6 +99,7 @@ kvm-objs := $(kvm-objs-m) $(kvm-objs-y) obj-$(CONFIG_KVM_440) += kvm.o obj-$(CONFIG_KVM_E500) += kvm.o +obj-$(CONFIG_KVM_E500MC) += kvm.o obj-$(CONFIG_KVM_BOOK3S_64) += kvm.o obj-$(CONFIG_KVM_BOOK3S_32) += kvm.o diff --git a/arch/powerpc/kvm/e500.h b/arch/powerpc/kvm/e500.h index ce3f163f702e..31430852ba74 100644 --- a/arch/powerpc/kvm/e500.h +++ b/arch/powerpc/kvm/e500.h @@ -2,7 +2,9 @@ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. All rights reserved. * * Author: Yu Liu + * Scott Wood * Ashish Kalra + * Varun Sethi * * Description: * This file is based on arch/powerpc/kvm/44x_tlb.h and @@ -100,6 +102,7 @@ static inline struct kvmppc_vcpu_e500 *to_e500(struct kvm_vcpu *vcpu) return container_of(vcpu, struct kvmppc_vcpu_e500, vcpu); } + /* This geometry is the legacy default -- can be overridden by userspace */ #define KVM_E500_TLB0_WAY_SIZE 128 #define KVM_E500_TLB0_WAY_NUM 2 @@ -250,10 +253,12 @@ static inline int tlbe_is_host_safe(const struct kvm_vcpu *vcpu, if (!get_tlb_v(tlbe)) return 0; +#ifndef CONFIG_KVM_BOOKE_HV /* Does it match current guest AS? */ /* XXX what about IS != DS? */ if (get_tlb_ts(tlbe) != !!(vcpu->arch.shared->msr & MSR_IS)) return 0; +#endif gpa = get_tlb_raddr(tlbe); if (!gfn_to_memslot(vcpu->kvm, gpa >> PAGE_SHIFT)) @@ -274,7 +279,11 @@ void kvmppc_e500_tlbil_one(struct kvmppc_vcpu_e500 *vcpu_e500, struct kvm_book3e_206_tlb_entry *gtlbe); void kvmppc_e500_tlbil_all(struct kvmppc_vcpu_e500 *vcpu_e500); -#ifdef CONFIG_KVM_E500 +#ifdef CONFIG_KVM_BOOKE_HV +#define kvmppc_e500_get_tlb_stid(vcpu, gtlbe) get_tlb_tid(gtlbe) +#define get_tlbmiss_tid(vcpu) get_cur_pid(vcpu) +#define get_tlb_sts(gtlbe) (gtlbe->mas1 & MAS1_TS) +#else unsigned int kvmppc_e500_get_tlb_stid(struct kvm_vcpu *vcpu, struct kvm_book3e_206_tlb_entry *gtlbe); @@ -288,6 +297,6 @@ static inline unsigned int get_tlbmiss_tid(struct kvm_vcpu *vcpu) /* Force TS=1 for all guest mappings. */ #define get_tlb_sts(gtlbe) (MAS1_TS) -#endif /* CONFIG_KVM_E500 */ +#endif /* !BOOKE_HV */ #endif /* KVM_E500_H */ diff --git a/arch/powerpc/kvm/e500_emulate.c b/arch/powerpc/kvm/e500_emulate.c index af02c18fc798..98b6c1cd6b82 100644 --- a/arch/powerpc/kvm/e500_emulate.c +++ b/arch/powerpc/kvm/e500_emulate.c @@ -85,6 +85,7 @@ int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) ulong spr_val = kvmppc_get_gpr(vcpu, rs); switch (sprn) { +#ifndef CONFIG_KVM_BOOKE_HV case SPRN_PID: kvmppc_set_pid(vcpu, spr_val); break; @@ -114,6 +115,7 @@ int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) vcpu->arch.shared->mas7_3 &= (u64)0xffffffff; vcpu->arch.shared->mas7_3 |= (u64)spr_val << 32; break; +#endif case SPRN_L1CSR0: vcpu_e500->l1csr0 = spr_val; vcpu_e500->l1csr0 &= ~(L1CSR0_DCFI | L1CSR0_CLFC); @@ -143,7 +145,14 @@ int kvmppc_core_emulate_mtspr(struct kvm_vcpu *vcpu, int sprn, int rs) case SPRN_IVOR35: vcpu->arch.ivor[BOOKE_IRQPRIO_PERFORMANCE_MONITOR] = spr_val; break; - +#ifdef CONFIG_KVM_BOOKE_HV + case SPRN_IVOR36: + vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL] = spr_val; + break; + case SPRN_IVOR37: + vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL_CRIT] = spr_val; + break; +#endif default: emulated = kvmppc_booke_emulate_mtspr(vcpu, sprn, rs); } @@ -155,9 +164,11 @@ int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt) { struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); int emulated = EMULATE_DONE; - unsigned long val; switch (sprn) { +#ifndef CONFIG_KVM_BOOKE_HV + unsigned long val; + case SPRN_PID: kvmppc_set_gpr(vcpu, rt, vcpu_e500->pid[0]); break; case SPRN_PID1: @@ -182,6 +193,7 @@ int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt) val = vcpu->arch.shared->mas7_3 >> 32; kvmppc_set_gpr(vcpu, rt, val); break; +#endif case SPRN_TLB0CFG: kvmppc_set_gpr(vcpu, rt, vcpu->arch.tlbcfg[0]); break; case SPRN_TLB1CFG: @@ -216,6 +228,14 @@ int kvmppc_core_emulate_mfspr(struct kvm_vcpu *vcpu, int sprn, int rt) case SPRN_IVOR35: kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_PERFORMANCE_MONITOR]); break; +#ifdef CONFIG_KVM_BOOKE_HV + case SPRN_IVOR36: + kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL]); + break; + case SPRN_IVOR37: + kvmppc_set_gpr(vcpu, rt, vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL_CRIT]); + break; +#endif default: emulated = kvmppc_booke_emulate_mfspr(vcpu, sprn, rt); } diff --git a/arch/powerpc/kvm/e500_tlb.c b/arch/powerpc/kvm/e500_tlb.c index 6eb5d655bdb4..e232bb464a80 100644 --- a/arch/powerpc/kvm/e500_tlb.c +++ b/arch/powerpc/kvm/e500_tlb.c @@ -2,7 +2,9 @@ * Copyright (C) 2008-2011 Freescale Semiconductor, Inc. All rights reserved. * * Author: Yu Liu, yu.liu@freescale.com + * Scott Wood, scottwood@freescale.com * Ashish Kalra, ashish.kalra@freescale.com + * Varun Sethi, varun.sethi@freescale.com * * Description: * This file is based on arch/powerpc/kvm/44x_tlb.c, @@ -64,6 +66,7 @@ static inline u32 e500_shadow_mas3_attrib(u32 mas3, int usermode) /* Mask off reserved bits. */ mas3 &= MAS3_ATTRIB_MASK; +#ifndef CONFIG_KVM_BOOKE_HV if (!usermode) { /* Guest is in supervisor mode, * so we need to translate guest @@ -71,8 +74,9 @@ static inline u32 e500_shadow_mas3_attrib(u32 mas3, int usermode) mas3 &= ~E500_TLB_USER_PERM_MASK; mas3 |= (mas3 & E500_TLB_SUPER_PERM_MASK) << 1; } - - return mas3 | E500_TLB_SUPER_PERM_MASK; + mas3 |= E500_TLB_SUPER_PERM_MASK; +#endif + return mas3; } static inline u32 e500_shadow_mas2_attrib(u32 mas2, int usermode) @@ -98,7 +102,16 @@ static inline void __write_host_tlbe(struct kvm_book3e_206_tlb_entry *stlbe, mtspr(SPRN_MAS2, (unsigned long)stlbe->mas2); mtspr(SPRN_MAS3, (u32)stlbe->mas7_3); mtspr(SPRN_MAS7, (u32)(stlbe->mas7_3 >> 32)); +#ifdef CONFIG_KVM_BOOKE_HV + mtspr(SPRN_MAS8, stlbe->mas8); +#endif asm volatile("isync; tlbwe" : : : "memory"); + +#ifdef CONFIG_KVM_BOOKE_HV + /* Must clear mas8 for other host tlbwe's */ + mtspr(SPRN_MAS8, 0); + isync(); +#endif local_irq_restore(flags); trace_kvm_booke206_stlb_write(mas0, stlbe->mas8, stlbe->mas1, @@ -384,6 +397,10 @@ static inline void kvmppc_e500_setup_stlbe( e500_shadow_mas2_attrib(gtlbe->mas2, pr); stlbe->mas7_3 = ((u64)pfn << PAGE_SHIFT) | e500_shadow_mas3_attrib(gtlbe->mas7_3, pr); + +#ifdef CONFIG_KVM_BOOKE_HV + stlbe->mas8 = MAS8_TGS | vcpu->kvm->arch.lpid; +#endif } static inline void kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500, diff --git a/arch/powerpc/kvm/e500mc.c b/arch/powerpc/kvm/e500mc.c new file mode 100644 index 000000000000..fe6c1de6b701 --- /dev/null +++ b/arch/powerpc/kvm/e500mc.c @@ -0,0 +1,342 @@ +/* + * Copyright (C) 2010 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Varun Sethi, + * + * Description: + * This file is derived from arch/powerpc/kvm/e500.c, + * by Yu Liu . + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2, as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "booke.h" +#include "e500.h" + +void kvmppc_set_pending_interrupt(struct kvm_vcpu *vcpu, enum int_class type) +{ + enum ppc_dbell dbell_type; + unsigned long tag; + + switch (type) { + case INT_CLASS_NONCRIT: + dbell_type = PPC_G_DBELL; + break; + case INT_CLASS_CRIT: + dbell_type = PPC_G_DBELL_CRIT; + break; + case INT_CLASS_MC: + dbell_type = PPC_G_DBELL_MC; + break; + default: + WARN_ONCE(1, "%s: unknown int type %d\n", __func__, type); + return; + } + + + tag = PPC_DBELL_LPID(vcpu->kvm->arch.lpid) | vcpu->vcpu_id; + mb(); + ppc_msgsnd(dbell_type, 0, tag); +} + +/* gtlbe must not be mapped by more than one host tlb entry */ +void kvmppc_e500_tlbil_one(struct kvmppc_vcpu_e500 *vcpu_e500, + struct kvm_book3e_206_tlb_entry *gtlbe) +{ + unsigned int tid, ts; + u32 val, eaddr, lpid; + unsigned long flags; + + ts = get_tlb_ts(gtlbe); + tid = get_tlb_tid(gtlbe); + lpid = vcpu_e500->vcpu.kvm->arch.lpid; + + /* We search the host TLB to invalidate its shadow TLB entry */ + val = (tid << 16) | ts; + eaddr = get_tlb_eaddr(gtlbe); + + local_irq_save(flags); + + mtspr(SPRN_MAS6, val); + mtspr(SPRN_MAS5, MAS5_SGS | lpid); + + asm volatile("tlbsx 0, %[eaddr]\n" : : [eaddr] "r" (eaddr)); + val = mfspr(SPRN_MAS1); + if (val & MAS1_VALID) { + mtspr(SPRN_MAS1, val & ~MAS1_VALID); + asm volatile("tlbwe"); + } + mtspr(SPRN_MAS5, 0); + /* NOTE: tlbsx also updates mas8, so clear it for host tlbwe */ + mtspr(SPRN_MAS8, 0); + isync(); + + local_irq_restore(flags); +} + +void kvmppc_e500_tlbil_all(struct kvmppc_vcpu_e500 *vcpu_e500) +{ + unsigned long flags; + + local_irq_save(flags); + mtspr(SPRN_MAS5, MAS5_SGS | vcpu_e500->vcpu.kvm->arch.lpid); + asm volatile("tlbilxlpid"); + mtspr(SPRN_MAS5, 0); + local_irq_restore(flags); +} + +void kvmppc_set_pid(struct kvm_vcpu *vcpu, u32 pid) +{ + vcpu->arch.pid = pid; +} + +void kvmppc_mmu_msr_notify(struct kvm_vcpu *vcpu, u32 old_msr) +{ +} + +void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu) +{ + struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); + + kvmppc_booke_vcpu_load(vcpu, cpu); + + mtspr(SPRN_LPID, vcpu->kvm->arch.lpid); + mtspr(SPRN_EPCR, vcpu->arch.shadow_epcr); + mtspr(SPRN_GPIR, vcpu->vcpu_id); + mtspr(SPRN_MSRP, vcpu->arch.shadow_msrp); + mtspr(SPRN_EPLC, vcpu->arch.eplc); + mtspr(SPRN_EPSC, vcpu->arch.epsc); + + mtspr(SPRN_GIVPR, vcpu->arch.ivpr); + mtspr(SPRN_GIVOR2, vcpu->arch.ivor[BOOKE_IRQPRIO_DATA_STORAGE]); + mtspr(SPRN_GIVOR8, vcpu->arch.ivor[BOOKE_IRQPRIO_SYSCALL]); + mtspr(SPRN_GSPRG0, (unsigned long)vcpu->arch.shared->sprg0); + mtspr(SPRN_GSPRG1, (unsigned long)vcpu->arch.shared->sprg1); + mtspr(SPRN_GSPRG2, (unsigned long)vcpu->arch.shared->sprg2); + mtspr(SPRN_GSPRG3, (unsigned long)vcpu->arch.shared->sprg3); + + mtspr(SPRN_GSRR0, vcpu->arch.shared->srr0); + mtspr(SPRN_GSRR1, vcpu->arch.shared->srr1); + + mtspr(SPRN_GEPR, vcpu->arch.epr); + mtspr(SPRN_GDEAR, vcpu->arch.shared->dar); + mtspr(SPRN_GESR, vcpu->arch.shared->esr); + + if (vcpu->arch.oldpir != mfspr(SPRN_PIR)) + kvmppc_e500_tlbil_all(vcpu_e500); + + kvmppc_load_guest_fp(vcpu); +} + +void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu) +{ + vcpu->arch.eplc = mfspr(SPRN_EPLC); + vcpu->arch.epsc = mfspr(SPRN_EPSC); + + vcpu->arch.shared->sprg0 = mfspr(SPRN_GSPRG0); + vcpu->arch.shared->sprg1 = mfspr(SPRN_GSPRG1); + vcpu->arch.shared->sprg2 = mfspr(SPRN_GSPRG2); + vcpu->arch.shared->sprg3 = mfspr(SPRN_GSPRG3); + + vcpu->arch.shared->srr0 = mfspr(SPRN_GSRR0); + vcpu->arch.shared->srr1 = mfspr(SPRN_GSRR1); + + vcpu->arch.epr = mfspr(SPRN_GEPR); + vcpu->arch.shared->dar = mfspr(SPRN_GDEAR); + vcpu->arch.shared->esr = mfspr(SPRN_GESR); + + vcpu->arch.oldpir = mfspr(SPRN_PIR); + + kvmppc_booke_vcpu_put(vcpu); +} + +int kvmppc_core_check_processor_compat(void) +{ + int r; + + if (strcmp(cur_cpu_spec->cpu_name, "e500mc") == 0) + r = 0; + else if (strcmp(cur_cpu_spec->cpu_name, "e5500") == 0) + r = 0; + else + r = -ENOTSUPP; + + return r; +} + +int kvmppc_core_vcpu_setup(struct kvm_vcpu *vcpu) +{ + struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); + + vcpu->arch.shadow_epcr = SPRN_EPCR_DSIGS | SPRN_EPCR_DGTMI | \ + SPRN_EPCR_DUVD; + vcpu->arch.shadow_msrp = MSRP_UCLEP | MSRP_DEP | MSRP_PMMP; + vcpu->arch.eplc = EPC_EGS | (vcpu->kvm->arch.lpid << EPC_ELPID_SHIFT); + vcpu->arch.epsc = vcpu->arch.eplc; + + vcpu->arch.pvr = mfspr(SPRN_PVR); + vcpu_e500->svr = mfspr(SPRN_SVR); + + vcpu->arch.cpu_type = KVM_CPU_E500MC; + + return 0; +} + +void kvmppc_core_get_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) +{ + struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); + + sregs->u.e.features |= KVM_SREGS_E_ARCH206_MMU | KVM_SREGS_E_PM | + KVM_SREGS_E_PC; + sregs->u.e.impl_id = KVM_SREGS_E_IMPL_FSL; + + sregs->u.e.impl.fsl.features = 0; + sregs->u.e.impl.fsl.svr = vcpu_e500->svr; + sregs->u.e.impl.fsl.hid0 = vcpu_e500->hid0; + sregs->u.e.impl.fsl.mcar = vcpu_e500->mcar; + + kvmppc_get_sregs_e500_tlb(vcpu, sregs); + + sregs->u.e.ivor_high[3] = + vcpu->arch.ivor[BOOKE_IRQPRIO_PERFORMANCE_MONITOR]; + sregs->u.e.ivor_high[4] = vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL]; + sregs->u.e.ivor_high[5] = vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL_CRIT]; + + kvmppc_get_sregs_ivor(vcpu, sregs); +} + +int kvmppc_core_set_sregs(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) +{ + struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); + int ret; + + if (sregs->u.e.impl_id == KVM_SREGS_E_IMPL_FSL) { + vcpu_e500->svr = sregs->u.e.impl.fsl.svr; + vcpu_e500->hid0 = sregs->u.e.impl.fsl.hid0; + vcpu_e500->mcar = sregs->u.e.impl.fsl.mcar; + } + + ret = kvmppc_set_sregs_e500_tlb(vcpu, sregs); + if (ret < 0) + return ret; + + if (!(sregs->u.e.features & KVM_SREGS_E_IVOR)) + return 0; + + if (sregs->u.e.features & KVM_SREGS_E_PM) { + vcpu->arch.ivor[BOOKE_IRQPRIO_PERFORMANCE_MONITOR] = + sregs->u.e.ivor_high[3]; + } + + if (sregs->u.e.features & KVM_SREGS_E_PC) { + vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL] = + sregs->u.e.ivor_high[4]; + vcpu->arch.ivor[BOOKE_IRQPRIO_DBELL_CRIT] = + sregs->u.e.ivor_high[5]; + } + + return kvmppc_set_sregs_ivor(vcpu, sregs); +} + +struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id) +{ + struct kvmppc_vcpu_e500 *vcpu_e500; + struct kvm_vcpu *vcpu; + int err; + + vcpu_e500 = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL); + if (!vcpu_e500) { + err = -ENOMEM; + goto out; + } + vcpu = &vcpu_e500->vcpu; + + /* Invalid PIR value -- this LPID dosn't have valid state on any cpu */ + vcpu->arch.oldpir = 0xffffffff; + + err = kvm_vcpu_init(vcpu, kvm, id); + if (err) + goto free_vcpu; + + err = kvmppc_e500_tlb_init(vcpu_e500); + if (err) + goto uninit_vcpu; + + vcpu->arch.shared = (void *)__get_free_page(GFP_KERNEL | __GFP_ZERO); + if (!vcpu->arch.shared) + goto uninit_tlb; + + return vcpu; + +uninit_tlb: + kvmppc_e500_tlb_uninit(vcpu_e500); +uninit_vcpu: + kvm_vcpu_uninit(vcpu); + +free_vcpu: + kmem_cache_free(kvm_vcpu_cache, vcpu_e500); +out: + return ERR_PTR(err); +} + +void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu) +{ + struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); + + free_page((unsigned long)vcpu->arch.shared); + kvmppc_e500_tlb_uninit(vcpu_e500); + kvm_vcpu_uninit(vcpu); + kmem_cache_free(kvm_vcpu_cache, vcpu_e500); +} + +int kvmppc_core_init_vm(struct kvm *kvm) +{ + int lpid; + + lpid = kvmppc_alloc_lpid(); + if (lpid < 0) + return lpid; + + kvm->arch.lpid = lpid; + return 0; +} + +void kvmppc_core_destroy_vm(struct kvm *kvm) +{ + kvmppc_free_lpid(kvm->arch.lpid); +} + +static int __init kvmppc_e500mc_init(void) +{ + int r; + + r = kvmppc_booke_init(); + if (r) + return r; + + kvmppc_init_lpid(64); + kvmppc_claim_lpid(0); /* host */ + + return kvm_init(NULL, sizeof(struct kvmppc_vcpu_e500), 0, THIS_MODULE); +} + +static void __exit kvmppc_e500mc_exit(void) +{ + kvmppc_booke_exit(); +} + +module_init(kvmppc_e500mc_init); +module_exit(kvmppc_e500mc_exit); diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 6a530e4b3e7c..14bf8d59bc2a 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -235,7 +235,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_PPC_PAIRED_SINGLES: case KVM_CAP_PPC_OSI: case KVM_CAP_PPC_GET_PVINFO: -#ifdef CONFIG_KVM_E500 +#if defined(CONFIG_KVM_E500) || defined(CONFIG_KVM_E500MC) case KVM_CAP_SW_TLB: #endif r = 1; @@ -629,7 +629,7 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, r = 0; vcpu->arch.papr_enabled = true; break; -#ifdef CONFIG_KVM_E500 +#if defined(CONFIG_KVM_E500) || defined(CONFIG_KVM_E500MC) case KVM_CAP_SW_TLB: { struct kvm_config_tlb cfg; void __user *user_ptr = (void __user *)(uintptr_t)cap->args[0]; @@ -706,7 +706,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp, break; } -#ifdef CONFIG_KVM_E500 +#if defined(CONFIG_KVM_E500) || defined(CONFIG_KVM_E500MC) case KVM_DIRTY_TLB: { struct kvm_dirty_tlb dirty; r = -EFAULT; -- cgit v1.2.3 From 4ab969199ec6a14604ceaffb21fe78cc4881d3b8 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 15 Feb 2012 13:28:48 +0000 Subject: KVM: PPC: e500mc: Add doorbell emulation support When one vcpu wants to kick another, it can issue a special IPI instruction called msgsnd. This patch emulates this instruction, its clearing counterpart and the infrastructure required to actually trigger that interrupt inside a guest vcpu. With this patch, SMP guests on e500mc work. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/dbell.h | 2 ++ arch/powerpc/kvm/booke.c | 2 ++ arch/powerpc/kvm/e500_emulate.c | 68 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) (limited to 'arch') diff --git a/arch/powerpc/include/asm/dbell.h b/arch/powerpc/include/asm/dbell.h index d7365b01f0c4..154c067761b1 100644 --- a/arch/powerpc/include/asm/dbell.h +++ b/arch/powerpc/include/asm/dbell.h @@ -19,7 +19,9 @@ #define PPC_DBELL_MSG_BRDCAST (0x04000000) #define PPC_DBELL_TYPE(x) (((x) & 0xf) << (63-36)) +#define PPC_DBELL_TYPE_MASK PPC_DBELL_TYPE(0xf) #define PPC_DBELL_LPID(x) ((x) << (63 - 49)) +#define PPC_DBELL_PIR_MASK 0x3fff enum ppc_dbell { PPC_DBELL = 0, /* doorbell */ PPC_DBELL_CRIT = 1, /* critical doorbell */ diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 0b77be187cf7..85bd5b8f3fd5 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -326,6 +326,7 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, int_class = INT_CLASS_NONCRIT; break; case BOOKE_IRQPRIO_CRITICAL: + case BOOKE_IRQPRIO_DBELL_CRIT: allowed = vcpu->arch.shared->msr & MSR_CE; allowed = allowed && !crit; msr_mask = MSR_GS | MSR_ME; @@ -342,6 +343,7 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, keep_irq = true; /* fall through */ case BOOKE_IRQPRIO_EXTERNAL: + case BOOKE_IRQPRIO_DBELL: allowed = vcpu->arch.shared->msr & MSR_EE; allowed = allowed && !crit; msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE; diff --git a/arch/powerpc/kvm/e500_emulate.c b/arch/powerpc/kvm/e500_emulate.c index 98b6c1cd6b82..99155f847a6a 100644 --- a/arch/powerpc/kvm/e500_emulate.c +++ b/arch/powerpc/kvm/e500_emulate.c @@ -14,16 +14,74 @@ #include #include +#include #include "booke.h" #include "e500.h" +#define XOP_MSGSND 206 +#define XOP_MSGCLR 238 #define XOP_TLBIVAX 786 #define XOP_TLBSX 914 #define XOP_TLBRE 946 #define XOP_TLBWE 978 #define XOP_TLBILX 18 +#ifdef CONFIG_KVM_E500MC +static int dbell2prio(ulong param) +{ + int msg = param & PPC_DBELL_TYPE_MASK; + int prio = -1; + + switch (msg) { + case PPC_DBELL_TYPE(PPC_DBELL): + prio = BOOKE_IRQPRIO_DBELL; + break; + case PPC_DBELL_TYPE(PPC_DBELL_CRIT): + prio = BOOKE_IRQPRIO_DBELL_CRIT; + break; + default: + break; + } + + return prio; +} + +static int kvmppc_e500_emul_msgclr(struct kvm_vcpu *vcpu, int rb) +{ + ulong param = vcpu->arch.gpr[rb]; + int prio = dbell2prio(param); + + if (prio < 0) + return EMULATE_FAIL; + + clear_bit(prio, &vcpu->arch.pending_exceptions); + return EMULATE_DONE; +} + +static int kvmppc_e500_emul_msgsnd(struct kvm_vcpu *vcpu, int rb) +{ + ulong param = vcpu->arch.gpr[rb]; + int prio = dbell2prio(rb); + int pir = param & PPC_DBELL_PIR_MASK; + int i; + struct kvm_vcpu *cvcpu; + + if (prio < 0) + return EMULATE_FAIL; + + kvm_for_each_vcpu(i, cvcpu, vcpu->kvm) { + int cpir = cvcpu->arch.shared->pir; + if ((param & PPC_DBELL_MSG_BRDCAST) || (cpir == pir)) { + set_bit(prio, &cvcpu->arch.pending_exceptions); + kvm_vcpu_kick(cvcpu); + } + } + + return EMULATE_DONE; +} +#endif + int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu, unsigned int inst, int *advance) { @@ -36,6 +94,16 @@ int kvmppc_core_emulate_op(struct kvm_run *run, struct kvm_vcpu *vcpu, case 31: switch (get_xop(inst)) { +#ifdef CONFIG_KVM_E500MC + case XOP_MSGSND: + emulated = kvmppc_e500_emul_msgsnd(vcpu, get_rb(inst)); + break; + + case XOP_MSGCLR: + emulated = kvmppc_e500_emul_msgclr(vcpu, get_rb(inst)); + break; +#endif + case XOP_TLBRE: emulated = kvmppc_e500_emul_tlbre(vcpu); break; -- cgit v1.2.3 From 79300f8cb9be201f916d075b3ef2e032d83a0d75 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 15 Feb 2012 19:12:29 +0000 Subject: KVM: PPC: e500mc: implicitly set MSR_GS When setting MSR for an e500mc guest, we implicitly always set MSR_GS to make sure the guest is in guest state. Since we have this implicit rule there, we don't need to explicitly pass MSR_GS to set_msr(). Remove all explicit setters of MSR_GS. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/booke.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 85bd5b8f3fd5..fcbe9281543f 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -280,7 +280,7 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, unsigned int priority) { int allowed = 0; - ulong uninitialized_var(msr_mask); + ulong msr_mask = 0; bool update_esr = false, update_dear = false; ulong crit_raw = vcpu->arch.shared->critical; ulong crit_r1 = kvmppc_get_gpr(vcpu, 1); @@ -322,20 +322,19 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, case BOOKE_IRQPRIO_AP_UNAVAIL: case BOOKE_IRQPRIO_ALIGNMENT: allowed = 1; - msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE; + msr_mask = MSR_CE | MSR_ME | MSR_DE; int_class = INT_CLASS_NONCRIT; break; case BOOKE_IRQPRIO_CRITICAL: case BOOKE_IRQPRIO_DBELL_CRIT: allowed = vcpu->arch.shared->msr & MSR_CE; allowed = allowed && !crit; - msr_mask = MSR_GS | MSR_ME; + msr_mask = MSR_ME; int_class = INT_CLASS_CRIT; break; case BOOKE_IRQPRIO_MACHINE_CHECK: allowed = vcpu->arch.shared->msr & MSR_ME; allowed = allowed && !crit; - msr_mask = MSR_GS; int_class = INT_CLASS_MC; break; case BOOKE_IRQPRIO_DECREMENTER: @@ -346,13 +345,13 @@ static int kvmppc_booke_irqprio_deliver(struct kvm_vcpu *vcpu, case BOOKE_IRQPRIO_DBELL: allowed = vcpu->arch.shared->msr & MSR_EE; allowed = allowed && !crit; - msr_mask = MSR_GS | MSR_CE | MSR_ME | MSR_DE; + msr_mask = MSR_CE | MSR_ME | MSR_DE; int_class = INT_CLASS_NONCRIT; break; case BOOKE_IRQPRIO_DEBUG: allowed = vcpu->arch.shared->msr & MSR_DE; allowed = allowed && !crit; - msr_mask = MSR_GS | MSR_ME; + msr_mask = MSR_ME; int_class = INT_CLASS_CRIT; break; } -- cgit v1.2.3 From a2723ce7fe4b99bc2df492067c3f81de2ee89aab Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 15 Feb 2012 23:06:24 +0000 Subject: KVM: PPC: e500mc: Move r1/r2 restoration very early If we hit any exception whatsoever in the restore path and r1/r2 aren't the host registers, we don't get a working oops. So it's always a good idea to restore them as early as possible. This time, it actually has practical reasons to do so too, since we need to have the host page fault handler fix up our guest instruction read code. And for that to work we need r1/r2 restored. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/bookehv_interrupts.S | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/bookehv_interrupts.S b/arch/powerpc/kvm/bookehv_interrupts.S index 9eaeebd86e44..63023ae14da4 100644 --- a/arch/powerpc/kvm/bookehv_interrupts.S +++ b/arch/powerpc/kvm/bookehv_interrupts.S @@ -67,6 +67,12 @@ * saved in vcpu: cr, ctr, r3-r13 */ .macro kvm_handler_common intno, srr0, flags + /* Restore host stack pointer */ + PPC_STL r1, VCPU_GPR(r1)(r4) + PPC_STL r2, VCPU_GPR(r2)(r4) + PPC_LL r1, VCPU_HOST_STACK(r4) + PPC_LL r2, HOST_R2(r1) + mfspr r10, SPRN_PID lwz r8, VCPU_HOST_PID(r4) PPC_LL r11, VCPU_SHARED(r4) @@ -290,10 +296,8 @@ _GLOBAL(kvmppc_resume_host) /* Save remaining volatile guest register state to vcpu. */ mfspr r3, SPRN_VRSAVE PPC_STL r0, VCPU_GPR(r0)(r4) - PPC_STL r1, VCPU_GPR(r1)(r4) mflr r5 mfspr r6, SPRN_SPRG4 - PPC_STL r2, VCPU_GPR(r2)(r4) PPC_STL r5, VCPU_LR(r4) mfspr r7, SPRN_SPRG5 PPC_STL r3, VCPU_VRSAVE(r4) @@ -334,10 +338,6 @@ _GLOBAL(kvmppc_resume_host) mtspr SPRN_EPCR, r3 isync - /* Restore host stack pointer */ - PPC_LL r1, VCPU_HOST_STACK(r4) - PPC_LL r2, HOST_R2(r1) - /* Switch to kernel stack and jump to handler. */ PPC_LL r3, HOST_RUN(r1) mr r5, r14 /* intno */ -- cgit v1.2.3 From 1d628af78a28c77143bcdd4ed09e93bb235d4198 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 15 Feb 2012 23:24:28 +0000 Subject: KVM: PPC: e500mc: add load inst fixup There's always a chance we're unable to read a guest instruction. The guest could have its TLB mapped execute-, but not readable, something odd happens and our TLB gets flushed. So it's a good idea to be prepared for that case and have a fallback that allows us to fix things up in that case. Add fixup code that keeps guest code from potentially crashing our host kernel. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/bookehv_interrupts.S | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/bookehv_interrupts.S b/arch/powerpc/kvm/bookehv_interrupts.S index 63023ae14da4..e9e735057939 100644 --- a/arch/powerpc/kvm/bookehv_interrupts.S +++ b/arch/powerpc/kvm/bookehv_interrupts.S @@ -28,6 +28,7 @@ #include #include #include +#include #include "../kernel/head_booke.h" /* for THREAD_NORMSAVE() */ @@ -171,9 +172,36 @@ PPC_STL r30, VCPU_GPR(r30)(r4) PPC_STL r31, VCPU_GPR(r31)(r4) mtspr SPRN_EPLC, r8 + + /* disable preemption, so we are sure we hit the fixup handler */ +#ifdef CONFIG_PPC64 + clrrdi r8,r1,THREAD_SHIFT +#else + rlwinm r8,r1,0,0,31-THREAD_SHIFT /* current thread_info */ +#endif + li r7, 1 + stw r7, TI_PREEMPT(r8) + isync - lwepx r9, 0, r5 + + /* + * In case the read goes wrong, we catch it and write an invalid value + * in LAST_INST instead. + */ +1: lwepx r9, 0, r5 +2: +.section .fixup, "ax" +3: li r9, KVM_INST_FETCH_FAILED + b 2b +.previous +.section __ex_table,"a" + PPC_LONG_ALIGN + PPC_LONG 1b,3b +.previous + mtspr SPRN_EPLC, r3 + li r7, 0 + stw r7, TI_PREEMPT(r8) stw r9, VCPU_LAST_INST(r4) .endif -- cgit v1.2.3 From bf7ca4bdcb8f1eb15d11879efa824b45443ddb69 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 15 Feb 2012 23:40:00 +0000 Subject: KVM: PPC: rename CONFIG_KVM_E500 -> CONFIG_KVM_E500V2 The CONFIG_KVM_E500 option really indicates that we're running on a V2 machine, not on a machine of the generic E500 class. So indicate that properly and change the config name accordingly. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/Kconfig | 8 ++++---- arch/powerpc/kvm/Makefile | 4 ++-- arch/powerpc/kvm/booke.c | 2 +- arch/powerpc/kvm/e500.h | 6 +++--- arch/powerpc/kvm/e500_tlb.c | 2 +- arch/powerpc/kvm/powerpc.c | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig index 58f6e6817924..44a998dc8399 100644 --- a/arch/powerpc/kvm/Kconfig +++ b/arch/powerpc/kvm/Kconfig @@ -109,7 +109,7 @@ config KVM_440 config KVM_EXIT_TIMING bool "Detailed exit timing" - depends on KVM_440 || KVM_E500 || KVM_E500MC + depends on KVM_440 || KVM_E500V2 || KVM_E500MC ---help--- Calculate elapsed time for every exit/enter cycle. A per-vcpu report is available in debugfs kvm/vm#_vcpu#_timing. @@ -118,14 +118,14 @@ config KVM_EXIT_TIMING If unsure, say N. -config KVM_E500 - bool "KVM support for PowerPC E500 processors" +config KVM_E500V2 + bool "KVM support for PowerPC E500v2 processors" depends on EXPERIMENTAL && E500 select KVM select KVM_MMIO ---help--- Support running unmodified E500 guest kernels in virtual machines on - E500 host processors. + E500v2 host processors. This module provides access to the hardware capabilities through a character device node named /dev/kvm. diff --git a/arch/powerpc/kvm/Makefile b/arch/powerpc/kvm/Makefile index 62febd730ba4..25225aea4c39 100644 --- a/arch/powerpc/kvm/Makefile +++ b/arch/powerpc/kvm/Makefile @@ -36,7 +36,7 @@ kvm-e500-objs := \ e500.o \ e500_tlb.o \ e500_emulate.o -kvm-objs-$(CONFIG_KVM_E500) := $(kvm-e500-objs) +kvm-objs-$(CONFIG_KVM_E500V2) := $(kvm-e500-objs) kvm-e500mc-objs := \ $(common-objs-y) \ @@ -98,7 +98,7 @@ kvm-objs-$(CONFIG_KVM_BOOK3S_32) := $(kvm-book3s_32-objs) kvm-objs := $(kvm-objs-m) $(kvm-objs-y) obj-$(CONFIG_KVM_440) += kvm.o -obj-$(CONFIG_KVM_E500) += kvm.o +obj-$(CONFIG_KVM_E500V2) += kvm.o obj-$(CONFIG_KVM_E500MC) += kvm.o obj-$(CONFIG_KVM_BOOK3S_64) += kvm.o obj-$(CONFIG_KVM_BOOK3S_32) += kvm.o diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index fcbe9281543f..9fcc7607a54e 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -762,7 +762,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, gpa_t gpaddr; gfn_t gfn; -#ifdef CONFIG_KVM_E500 +#ifdef CONFIG_KVM_E500V2 if (!(vcpu->arch.shared->msr & MSR_PR) && (eaddr & PAGE_MASK) == vcpu->arch.magic_page_ea) { kvmppc_map_magic(vcpu); diff --git a/arch/powerpc/kvm/e500.h b/arch/powerpc/kvm/e500.h index 31430852ba74..7967f3f10a16 100644 --- a/arch/powerpc/kvm/e500.h +++ b/arch/powerpc/kvm/e500.h @@ -39,7 +39,7 @@ struct tlbe_priv { struct tlbe_ref ref; /* TLB0 only -- TLB1 uses tlb_refs */ }; -#ifdef CONFIG_KVM_E500 +#ifdef CONFIG_KVM_E500V2 struct vcpu_id_table; #endif @@ -89,7 +89,7 @@ struct kvmppc_vcpu_e500 { u64 *g2h_tlb1_map; unsigned int *h2g_tlb1_rmap; -#ifdef CONFIG_KVM_E500 +#ifdef CONFIG_KVM_E500V2 u32 pid[E500_PID_NUM]; /* vcpu id table */ @@ -136,7 +136,7 @@ void kvmppc_get_sregs_e500_tlb(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs); int kvmppc_set_sregs_e500_tlb(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs); -#ifdef CONFIG_KVM_E500 +#ifdef CONFIG_KVM_E500V2 unsigned int kvmppc_e500_get_sid(struct kvmppc_vcpu_e500 *vcpu_e500, unsigned int as, unsigned int gid, unsigned int pr, int avoid_recursion); diff --git a/arch/powerpc/kvm/e500_tlb.c b/arch/powerpc/kvm/e500_tlb.c index e232bb464a80..279e10aaf7b3 100644 --- a/arch/powerpc/kvm/e500_tlb.c +++ b/arch/powerpc/kvm/e500_tlb.c @@ -156,7 +156,7 @@ static inline void write_host_tlbe(struct kvmppc_vcpu_e500 *vcpu_e500, } } -#ifdef CONFIG_KVM_E500 +#ifdef CONFIG_KVM_E500V2 void kvmppc_map_magic(struct kvm_vcpu *vcpu) { struct kvmppc_vcpu_e500 *vcpu_e500 = to_e500(vcpu); diff --git a/arch/powerpc/kvm/powerpc.c b/arch/powerpc/kvm/powerpc.c index 14bf8d59bc2a..58ad8609bb43 100644 --- a/arch/powerpc/kvm/powerpc.c +++ b/arch/powerpc/kvm/powerpc.c @@ -79,7 +79,7 @@ int kvmppc_kvm_pv(struct kvm_vcpu *vcpu) } case HC_VENDOR_KVM | KVM_HC_FEATURES: r = HC_EV_SUCCESS; -#if defined(CONFIG_PPC_BOOK3S) || defined(CONFIG_KVM_E500) +#if defined(CONFIG_PPC_BOOK3S) || defined(CONFIG_KVM_E500V2) /* XXX Missing magic page on 44x */ r2 |= (1 << KVM_FEATURE_MAGIC_PAGE); #endif @@ -235,7 +235,7 @@ int kvm_dev_ioctl_check_extension(long ext) case KVM_CAP_PPC_PAIRED_SINGLES: case KVM_CAP_PPC_OSI: case KVM_CAP_PPC_GET_PVINFO: -#if defined(CONFIG_KVM_E500) || defined(CONFIG_KVM_E500MC) +#if defined(CONFIG_KVM_E500V2) || defined(CONFIG_KVM_E500MC) case KVM_CAP_SW_TLB: #endif r = 1; @@ -629,7 +629,7 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, r = 0; vcpu->arch.papr_enabled = true; break; -#if defined(CONFIG_KVM_E500) || defined(CONFIG_KVM_E500MC) +#if defined(CONFIG_KVM_E500V2) || defined(CONFIG_KVM_E500MC) case KVM_CAP_SW_TLB: { struct kvm_config_tlb cfg; void __user *user_ptr = (void __user *)(uintptr_t)cap->args[0]; @@ -706,7 +706,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp, break; } -#if defined(CONFIG_KVM_E500) || defined(CONFIG_KVM_E500MC) +#if defined(CONFIG_KVM_E500V2) || defined(CONFIG_KVM_E500MC) case KVM_DIRTY_TLB: { struct kvm_dirty_tlb dirty; r = -EFAULT; -- cgit v1.2.3 From b2e19b20708edb6413dea38e6285a6e546dce06b Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 15 Feb 2012 23:41:20 +0000 Subject: KVM: PPC: make e500v2 kvm and e500mc cpu mutually exclusive We can't run e500v2 kvm on e500mc kernels, so indicate that by making the 2 options mutually exclusive in kconfig. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/Kconfig b/arch/powerpc/kvm/Kconfig index 44a998dc8399..f4dacb9c57fa 100644 --- a/arch/powerpc/kvm/Kconfig +++ b/arch/powerpc/kvm/Kconfig @@ -120,7 +120,7 @@ config KVM_EXIT_TIMING config KVM_E500V2 bool "KVM support for PowerPC E500v2 processors" - depends on EXPERIMENTAL && E500 + depends on EXPERIMENTAL && E500 && !PPC_E500MC select KVM select KVM_MMIO ---help--- -- cgit v1.2.3 From acab05290696db0a5431a9ad171be649ab56e87b Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 16 Feb 2012 13:07:32 +0000 Subject: KVM: PPC: booke: remove leftover debugging The e500mc patches left some debug code in that we don't need. Remove it. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/booke.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 9fcc7607a54e..17d5318e6e4f 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -469,11 +469,6 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) return -EINVAL; } - if (!current->thread.kvm_vcpu) { - WARN(1, "no vcpu\n"); - return -EPERM; - } - local_irq_disable(); kvmppc_core_prepare_to_enter(vcpu); -- cgit v1.2.3 From d1ff54992d3008f4253ab3176913bb85d770e935 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 16 Feb 2012 13:24:03 +0000 Subject: KVM: PPC: booke: deliver program int on emulation failure When we fail to emulate an instruction for the guest, we better go in and tell it that we failed to emulate it, by throwing an illegal instruction exception. Please beware that we basically never get around to telling the guest that we failed thanks to the debugging code right above it. If user space however decides that it wants to ignore the debug, we would at least do "the right thing" afterwards. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/booke.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 17d5318e6e4f..9979be1d7ff2 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -545,13 +545,13 @@ static int emulation_exit(struct kvm_run *run, struct kvm_vcpu *vcpu) return RESUME_HOST; case EMULATE_FAIL: - /* XXX Deliver Program interrupt to guest. */ printk(KERN_CRIT "%s: emulation at %lx failed (%08x)\n", __func__, vcpu->arch.pc, vcpu->arch.last_inst); /* For debugging, encode the failing instruction and * report it to userspace. */ run->hw.hardware_exit_reason = ~0ULL << 32; run->hw.hardware_exit_reason |= vcpu->arch.last_inst; + kvmppc_core_queue_program(vcpu, ESR_PIL); return RESUME_HOST; default: -- cgit v1.2.3 From a8e4ef841429d338b8700998afb3dfc18c1f25d9 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 16 Feb 2012 14:07:37 +0000 Subject: KVM: PPC: booke: rework rescheduling checks Instead of checking whether we should reschedule only when we exited due to an interrupt, let's always check before entering the guest back again. This gets the target more in line with the other archs. Also while at it, generalize the whole thing so that eventually we could have a single kvmppc_prepare_to_enter function for all ppc targets that does signal and reschedule checking for us. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/kvm_ppc.h | 2 +- arch/powerpc/kvm/book3s.c | 4 ++- arch/powerpc/kvm/booke.c | 72 ++++++++++++++++++++++++++------------ 3 files changed, 54 insertions(+), 24 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/include/asm/kvm_ppc.h b/arch/powerpc/include/asm/kvm_ppc.h index e709975702a6..7f0a3dae7cde 100644 --- a/arch/powerpc/include/asm/kvm_ppc.h +++ b/arch/powerpc/include/asm/kvm_ppc.h @@ -95,7 +95,7 @@ extern int kvmppc_core_vcpu_translate(struct kvm_vcpu *vcpu, extern void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu); extern void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu); -extern void kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu); +extern int kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu); extern int kvmppc_core_pending_dec(struct kvm_vcpu *vcpu); extern void kvmppc_core_queue_program(struct kvm_vcpu *vcpu, ulong flags); extern void kvmppc_core_queue_dec(struct kvm_vcpu *vcpu); diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c index 7d54f4ed6d96..c8ead7b9f03b 100644 --- a/arch/powerpc/kvm/book3s.c +++ b/arch/powerpc/kvm/book3s.c @@ -258,7 +258,7 @@ static bool clear_irqprio(struct kvm_vcpu *vcpu, unsigned int priority) return true; } -void kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu) +int kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu) { unsigned long *pending = &vcpu->arch.pending_exceptions; unsigned long old_pending = vcpu->arch.pending_exceptions; @@ -283,6 +283,8 @@ void kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu) /* Tell the guest about our interrupt status */ kvmppc_update_int_pending(vcpu, *pending, old_pending); + + return 0; } pfn_t kvmppc_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn) diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 9979be1d7ff2..3da0e4273389 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -439,8 +439,9 @@ static void kvmppc_core_check_exceptions(struct kvm_vcpu *vcpu) } /* Check pending exceptions and deliver one, if possible. */ -void kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu) +int kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu) { + int r = 0; WARN_ON_ONCE(!irqs_disabled()); kvmppc_core_check_exceptions(vcpu); @@ -451,8 +452,46 @@ void kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu) local_irq_disable(); kvmppc_set_exit_type(vcpu, EMULATED_MTMSRWE_EXITS); - kvmppc_core_check_exceptions(vcpu); + r = 1; }; + + return r; +} + +/* + * Common checks before entering the guest world. Call with interrupts + * disabled. + * + * returns !0 if a signal is pending and check_signal is true + */ +static int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu, bool check_signal) +{ + int r = 0; + + WARN_ON_ONCE(!irqs_disabled()); + while (true) { + if (need_resched()) { + local_irq_enable(); + cond_resched(); + local_irq_disable(); + continue; + } + + if (check_signal && signal_pending(current)) { + r = 1; + break; + } + + if (kvmppc_core_prepare_to_enter(vcpu)) { + /* interrupts got enabled in between, so we + are back at square 1 */ + continue; + } + + break; + } + + return r; } int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) @@ -470,10 +509,7 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) } local_irq_disable(); - - kvmppc_core_prepare_to_enter(vcpu); - - if (signal_pending(current)) { + if (kvmppc_prepare_to_enter(vcpu, true)) { kvm_run->exit_reason = KVM_EXIT_INTR; ret = -EINTR; goto out; @@ -598,25 +634,21 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, switch (exit_nr) { case BOOKE_INTERRUPT_MACHINE_CHECK: - kvm_resched(vcpu); r = RESUME_GUEST; break; case BOOKE_INTERRUPT_EXTERNAL: kvmppc_account_exit(vcpu, EXT_INTR_EXITS); - kvm_resched(vcpu); r = RESUME_GUEST; break; case BOOKE_INTERRUPT_DECREMENTER: kvmppc_account_exit(vcpu, DEC_EXITS); - kvm_resched(vcpu); r = RESUME_GUEST; break; case BOOKE_INTERRUPT_DOORBELL: kvmppc_account_exit(vcpu, DBELL_EXITS); - kvm_resched(vcpu); r = RESUME_GUEST; break; @@ -865,19 +897,15 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, BUG(); } + /* + * To avoid clobbering exit_reason, only check for signals if we + * aren't already exiting to userspace for some other reason. + */ local_irq_disable(); - - kvmppc_core_prepare_to_enter(vcpu); - - if (!(r & RESUME_HOST)) { - /* To avoid clobbering exit_reason, only check for signals if - * we aren't already exiting to userspace for some other - * reason. */ - if (signal_pending(current)) { - run->exit_reason = KVM_EXIT_INTR; - r = (-EINTR << 2) | RESUME_HOST | (r & RESUME_FLAG_NV); - kvmppc_account_exit(vcpu, SIGNAL_EXITS); - } + if (kvmppc_prepare_to_enter(vcpu, !(r & RESUME_HOST))) { + run->exit_reason = KVM_EXIT_INTR; + r = (-EINTR << 2) | RESUME_HOST | (r & RESUME_FLAG_NV); + kvmppc_account_exit(vcpu, SIGNAL_EXITS); } return r; -- cgit v1.2.3 From 8b3a00fcd3c9ea4e2cbae12af3cd8c9d7d1e109a Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 16 Feb 2012 14:12:46 +0000 Subject: KVM: PPC: booke: BOOKE_IRQPRIO_MAX is n+1 The semantics of BOOKE_IRQPRIO_MAX changed to denote the highest available irqprio + 1, so let's reflect that in the code too. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/booke.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 3da0e4273389..11b06251e42e 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -425,7 +425,7 @@ static void kvmppc_core_check_exceptions(struct kvm_vcpu *vcpu) } priority = __ffs(*pending); - while (priority <= BOOKE_IRQPRIO_MAX) { + while (priority < BOOKE_IRQPRIO_MAX) { if (kvmppc_booke_irqprio_deliver(vcpu, priority)) break; -- cgit v1.2.3 From 73ede8d32be6adc298fe3c2716e77c352c504c8c Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 16 Feb 2012 14:34:55 +0000 Subject: KVM: PPC: bookehv: fix exit timing When using exit timing stats, we clobber r9 in the NEED_EMU case, so better move that part down a few lines and fix it that way. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/bookehv_interrupts.S | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/bookehv_interrupts.S b/arch/powerpc/kvm/bookehv_interrupts.S index e9e735057939..e4a117263357 100644 --- a/arch/powerpc/kvm/bookehv_interrupts.S +++ b/arch/powerpc/kvm/bookehv_interrupts.S @@ -83,10 +83,6 @@ stw r10, VCPU_GUEST_PID(r4) mtspr SPRN_PID, r8 - .if \flags & NEED_EMU - lwz r9, VCPU_KVM(r4) - .endif - #ifdef CONFIG_KVM_EXIT_TIMING /* save exit time */ 1: mfspr r7, SPRN_TBRU @@ -98,6 +94,10 @@ PPC_STL r9, VCPU_TIMING_EXIT_TBU(r4) #endif + .if \flags & NEED_EMU + lwz r9, VCPU_KVM(r4) + .endif + oris r8, r6, MSR_CE@h #ifndef CONFIG_64BIT stw r6, (VCPU_SHARED_MSR + 4)(r11) -- cgit v1.2.3 From 8764b46ee3873b685a7823fc79388bae7d19e51e Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 16 Feb 2012 14:40:26 +0000 Subject: KVM: PPC: bookehv: remove negation for CONFIG_64BIT Instead if doing #ifndef CONFIG_64BIT ... #else ... #endif we should rather do #ifdef CONFIG_64BIT ... #else ... #endif which is a lot easier to read. Change the bookehv implementation to stick with this rule. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/bookehv_interrupts.S | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/bookehv_interrupts.S b/arch/powerpc/kvm/bookehv_interrupts.S index e4a117263357..af771de7d30b 100644 --- a/arch/powerpc/kvm/bookehv_interrupts.S +++ b/arch/powerpc/kvm/bookehv_interrupts.S @@ -99,10 +99,10 @@ .endif oris r8, r6, MSR_CE@h -#ifndef CONFIG_64BIT - stw r6, (VCPU_SHARED_MSR + 4)(r11) -#else +#ifdef CONFIG_64BIT std r6, (VCPU_SHARED_MSR)(r11) +#else + stw r6, (VCPU_SHARED_MSR + 4)(r11) #endif ori r8, r8, MSR_ME | MSR_RI PPC_STL r5, VCPU_PC(r4) @@ -344,10 +344,10 @@ _GLOBAL(kvmppc_resume_host) stw r5, VCPU_SHARED_MAS0(r11) mfspr r7, SPRN_MAS2 stw r6, VCPU_SHARED_MAS1(r11) -#ifndef CONFIG_64BIT - stw r7, (VCPU_SHARED_MAS2 + 4)(r11) -#else +#ifdef CONFIG_64BIT std r7, (VCPU_SHARED_MAS2)(r11) +#else + stw r7, (VCPU_SHARED_MAS2 + 4)(r11) #endif mfspr r5, SPRN_MAS3 mfspr r6, SPRN_MAS4 @@ -530,10 +530,10 @@ lightweight_exit: stw r3, VCPU_HOST_MAS6(r4) lwz r3, VCPU_SHARED_MAS0(r11) lwz r5, VCPU_SHARED_MAS1(r11) -#ifndef CONFIG_64BIT - lwz r6, (VCPU_SHARED_MAS2 + 4)(r11) -#else +#ifdef CONFIG_64BIT ld r6, (VCPU_SHARED_MAS2)(r11) +#else + lwz r6, (VCPU_SHARED_MAS2 + 4)(r11) #endif lwz r7, VCPU_SHARED_MAS7_3+4(r11) lwz r8, VCPU_SHARED_MAS4(r11) @@ -572,10 +572,10 @@ lightweight_exit: PPC_LL r6, VCPU_CTR(r4) PPC_LL r7, VCPU_CR(r4) PPC_LL r8, VCPU_PC(r4) -#ifndef CONFIG_64BIT - lwz r9, (VCPU_SHARED_MSR + 4)(r11) -#else +#ifdef CONFIG_64BIT ld r9, (VCPU_SHARED_MSR)(r11) +#else + lwz r9, (VCPU_SHARED_MSR + 4)(r11) #endif PPC_LL r0, VCPU_GPR(r0)(r4) PPC_LL r1, VCPU_GPR(r1)(r4) -- cgit v1.2.3 From 8a3da55784cf2aea8ef6acdd1f50e5ad52f76574 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 16 Feb 2012 14:45:33 +0000 Subject: KVM: PPC: bookehv: remove SET_VCPU The SET_VCPU macro is a leftover from times when the vcpu struct wasn't stored in the thread on vcpu_load/put. It's not needed anymore. Remove it. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/bookehv_interrupts.S | 8 -------- 1 file changed, 8 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/bookehv_interrupts.S b/arch/powerpc/kvm/bookehv_interrupts.S index af771de7d30b..dfa606dedb47 100644 --- a/arch/powerpc/kvm/bookehv_interrupts.S +++ b/arch/powerpc/kvm/bookehv_interrupts.S @@ -35,9 +35,6 @@ #define GET_VCPU(vcpu, thread) \ PPC_LL vcpu, THREAD_KVM_VCPU(thread) -#define SET_VCPU(vcpu) \ - PPC_STL vcpu, (THREAD + THREAD_KVM_VCPU)(r2) - #define LONGBYTES (BITS_PER_LONG / 8) #define VCPU_GPR(n) (VCPU_GPRS + (n * LONGBYTES)) @@ -517,11 +514,6 @@ lightweight_exit: lwz r3, VCPU_GUEST_PID(r4) mtspr SPRN_PID, r3 - /* Save vcpu pointer for the exception handlers - * must be done before loading guest r2. - */ -// SET_VCPU(r4) - PPC_LL r11, VCPU_SHARED(r4) /* Save host mas4 and mas6 and load guest MAS registers */ mfspr r3, SPRN_MAS4 -- cgit v1.2.3 From e9ba39c1f3dff93efddacbd4569ada05633e2a9b Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 16 Feb 2012 14:53:04 +0000 Subject: KVM: PPC: bookehv: disable MAS register updates early We need to make sure that no MAS updates happen automatically while we have the guest MAS registers loaded. So move the disabling code a bit higher up so that it covers the full time we have guest values in MAS registers. The race this patch fixes should never occur, but it makes the code a bit more logical to do it this way around. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/bookehv_interrupts.S | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/bookehv_interrupts.S b/arch/powerpc/kvm/bookehv_interrupts.S index dfa606dedb47..3a1db909947f 100644 --- a/arch/powerpc/kvm/bookehv_interrupts.S +++ b/arch/powerpc/kvm/bookehv_interrupts.S @@ -358,6 +358,7 @@ _GLOBAL(kvmppc_resume_host) mtspr SPRN_MAS4, r6 stw r5, VCPU_SHARED_MAS7_3+0(r11) mtspr SPRN_MAS6, r8 + /* Enable MAS register updates via exception */ mfspr r3, SPRN_EPCR rlwinm r3, r3, 0, ~SPRN_EPCR_DMIUH mtspr SPRN_EPCR, r3 @@ -515,6 +516,11 @@ lightweight_exit: mtspr SPRN_PID, r3 PPC_LL r11, VCPU_SHARED(r4) + /* Disable MAS register updates via exception */ + mfspr r3, SPRN_EPCR + oris r3, r3, SPRN_EPCR_DMIUH@h + mtspr SPRN_EPCR, r3 + isync /* Save host mas4 and mas6 and load guest MAS registers */ mfspr r3, SPRN_MAS4 stw r3, VCPU_HOST_MAS4(r4) @@ -538,10 +544,6 @@ lightweight_exit: lwz r5, VCPU_SHARED_MAS7_3+0(r11) mtspr SPRN_MAS6, r3 mtspr SPRN_MAS7, r5 - /* Disable MAS register updates via exception */ - mfspr r3, SPRN_EPCR - oris r3, r3, SPRN_EPCR_DMIUH@h - mtspr SPRN_EPCR, r3 /* * Host interrupt handlers may have clobbered these guest-readable -- cgit v1.2.3 From 5fd8505ea4b7456d57eacefbf00b669f15f5f0c0 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Thu, 16 Feb 2012 15:04:54 +0000 Subject: KVM: PPC: bookehv: add comment about shadow_msr For BookE HV the guest visible MSR is shared->msr and is identical to the MSR that is in use while the guest is running, because we can't trap reads from/to MSR. So shadow_msr is unused there. Indicate that with a comment. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/kvm_host.h | 1 + 1 file changed, 1 insertion(+) (limited to 'arch') diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index e645623728fc..97ecdaf82956 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -386,6 +386,7 @@ struct kvm_vcpu_arch { #endif u32 vrsave; /* also USPRG0 */ u32 mmucr; + /* shadow_msr is unused for BookE HV */ ulong shadow_msr; ulong csrr0; ulong csrr1; -- cgit v1.2.3 From c35c9d84cf141ebf05e2e481c0faccedc4f8f7ff Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 20 Feb 2012 12:21:18 +0100 Subject: KVM: PPC: booke: Readd debug abort code for machine check When during guest execution we get a machine check interrupt, we don't know how to handle it yet. So let's add the error printing code back again that we dropped accidently earlier and tell user space that something went really wrong. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/booke.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 11b06251e42e..af02d9dbd951 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -634,7 +634,12 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, switch (exit_nr) { case BOOKE_INTERRUPT_MACHINE_CHECK: - r = RESUME_GUEST; + printk("MACHINE CHECK: %lx\n", mfspr(SPRN_MCSR)); + kvmppc_dump_vcpu(vcpu); + /* For debugging, send invalid exit reason to user space */ + run->hw.hardware_exit_reason = ~1ULL << 32; + run->hw.hardware_exit_reason |= mfspr(SPRN_MCSR); + r = RESUME_HOST; break; case BOOKE_INTERRUPT_EXTERNAL: -- cgit v1.2.3 From 0268597c811ccf55e0bda20907c1a9e6001365cf Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 20 Feb 2012 12:33:22 +0100 Subject: KVM: PPC: booke: add GS documentation for program interrupt The comment for program interrupts triggered when using bookehv was misleading. Update it to mention why MSR_GS indicates that we have to inject an interrupt into the guest again, not emulate it. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/booke.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index af02d9dbd951..7df3f3ae71d9 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -685,8 +685,14 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, case BOOKE_INTERRUPT_PROGRAM: if (vcpu->arch.shared->msr & (MSR_PR | MSR_GS)) { - /* Program traps generated by user-level software must be handled - * by the guest kernel. */ + /* + * Program traps generated by user-level software must + * be handled by the guest kernel. + * + * In GS mode, hypervisor privileged instructions trap + * on BOOKE_INTERRUPT_HV_PRIV, not here, so these are + * actual program interrupts, handled by the guest. + */ kvmppc_core_queue_program(vcpu, vcpu->arch.fault_esr); r = RESUME_GUEST; kvmppc_account_exit(vcpu, USR_PR_INST); -- cgit v1.2.3 From 55cdf08b9a6c99f1335fa6ad42372dcfb3715b56 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 20 Feb 2012 12:39:36 +0100 Subject: KVM: PPC: bookehv: remove unused code There was some unused code in the exit code path that must have been a leftover from earlier iterations. While it did no harm, it's superfluous and thus should be removed. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/bookehv_interrupts.S | 7 ------- 1 file changed, 7 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/bookehv_interrupts.S b/arch/powerpc/kvm/bookehv_interrupts.S index 3a1db909947f..2d1f56cad48c 100644 --- a/arch/powerpc/kvm/bookehv_interrupts.S +++ b/arch/powerpc/kvm/bookehv_interrupts.S @@ -91,10 +91,6 @@ PPC_STL r9, VCPU_TIMING_EXIT_TBU(r4) #endif - .if \flags & NEED_EMU - lwz r9, VCPU_KVM(r4) - .endif - oris r8, r6, MSR_CE@h #ifdef CONFIG_64BIT std r6, (VCPU_SHARED_MSR)(r11) @@ -112,9 +108,6 @@ * appropriate for the exception type). */ cmpw r6, r8 - .if \flags & NEED_EMU - lwz r9, KVM_LPID(r9) - .endif beq 1f mfmsr r7 .if \srr0 != SPRN_MCSRR0 && \srr0 != SPRN_CSRR0 -- cgit v1.2.3 From c6b3733bef2ffece49336dba7220fefdae5fa908 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 20 Feb 2012 17:48:47 +0100 Subject: KVM: PPC: e500: fix typo in tlb code The tlbncfg registers should be populated with their respective TLB's values. Fix the obvious typo. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/e500_tlb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/e500_tlb.c b/arch/powerpc/kvm/e500_tlb.c index 279e10aaf7b3..e05232b746ff 100644 --- a/arch/powerpc/kvm/e500_tlb.c +++ b/arch/powerpc/kvm/e500_tlb.c @@ -1268,8 +1268,8 @@ int kvmppc_e500_tlb_init(struct kvmppc_vcpu_e500 *vcpu_e500) vcpu->arch.tlbcfg[1] = mfspr(SPRN_TLB1CFG) & ~(TLBnCFG_N_ENTRY | TLBnCFG_ASSOC); - vcpu->arch.tlbcfg[0] |= vcpu_e500->gtlb_params[1].entries; - vcpu->arch.tlbcfg[0] |= + vcpu->arch.tlbcfg[1] |= vcpu_e500->gtlb_params[1].entries; + vcpu->arch.tlbcfg[1] |= vcpu_e500->gtlb_params[1].ways << TLBnCFG_ASSOC_SHIFT; return 0; -- cgit v1.2.3 From 95f2e921446dbc2e9a785734049ee349a67434bd Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 20 Feb 2012 22:45:12 +0100 Subject: KVM: PPC: booke: Support perfmon interrupts When during guest context we get a performance monitor interrupt, we currently bail out and oops. Let's route it to its correct handler instead. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/booke.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'arch') diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 7df3f3ae71d9..ee39c8a80c63 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -679,6 +679,10 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, r = RESUME_GUEST; break; + case BOOKE_INTERRUPT_PERFORMANCE_MONITOR: + r = RESUME_GUEST; + break; + case BOOKE_INTERRUPT_HV_PRIV: r = emulation_exit(run, vcpu); break; -- cgit v1.2.3 From 4e642ccbd6a3f1410155c7700f54b56b6c7df9a2 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 20 Feb 2012 23:57:26 +0100 Subject: KVM: PPC: booke: expose good state on irq reinject When reinjecting an interrupt into the host interrupt handler after we're back in host kernel land, we need to tell the kernel where the interrupt happened. We can't tell it that we were in guest state, because that might lead to random code walking host addresses. So instead, we tell it that we came from the interrupt reinject code. This helps getting reasonable numbers out of perf. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/booke.c | 56 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 15 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index ee39c8a80c63..488936bece69 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -595,37 +595,63 @@ static int emulation_exit(struct kvm_run *run, struct kvm_vcpu *vcpu) } } -/** - * kvmppc_handle_exit - * - * Return value is in the form (errcode<<2 | RESUME_FLAG_HOST | RESUME_FLAG_NV) - */ -int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, - unsigned int exit_nr) +static void kvmppc_fill_pt_regs(struct pt_regs *regs) { - int r = RESUME_HOST; + ulong r1, ip, msr, lr; + + asm("mr %0, 1" : "=r"(r1)); + asm("mflr %0" : "=r"(lr)); + asm("mfmsr %0" : "=r"(msr)); + asm("bl 1f; 1: mflr %0" : "=r"(ip)); + + memset(regs, 0, sizeof(*regs)); + regs->gpr[1] = r1; + regs->nip = ip; + regs->msr = msr; + regs->link = lr; +} - /* update before a new last_exit_type is rewritten */ - kvmppc_update_timing_stats(vcpu); +static void kvmppc_restart_interrupt(struct kvm_vcpu *vcpu, + unsigned int exit_nr) +{ + struct pt_regs regs; switch (exit_nr) { case BOOKE_INTERRUPT_EXTERNAL: - do_IRQ(current->thread.regs); + kvmppc_fill_pt_regs(®s); + do_IRQ(®s); break; - case BOOKE_INTERRUPT_DECREMENTER: - timer_interrupt(current->thread.regs); + kvmppc_fill_pt_regs(®s); + timer_interrupt(®s); break; - #if defined(CONFIG_PPC_FSL_BOOK3E) || defined(CONFIG_PPC_BOOK3E_64) case BOOKE_INTERRUPT_DOORBELL: - doorbell_exception(current->thread.regs); + kvmppc_fill_pt_regs(®s); + doorbell_exception(®s); break; #endif case BOOKE_INTERRUPT_MACHINE_CHECK: /* FIXME */ break; } +} + +/** + * kvmppc_handle_exit + * + * Return value is in the form (errcode<<2 | RESUME_FLAG_HOST | RESUME_FLAG_NV) + */ +int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, + unsigned int exit_nr) +{ + int r = RESUME_HOST; + + /* update before a new last_exit_type is rewritten */ + kvmppc_update_timing_stats(vcpu); + + /* restart interrupts if they were meant for the host */ + kvmppc_restart_interrupt(vcpu, exit_nr); local_irq_enable(); -- cgit v1.2.3 From 7cc1e8ee78f469ecff8aa29465325f1e4c5e1b5f Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 22 Feb 2012 16:26:34 +0100 Subject: KVM: PPC: booke: Reinject performance monitor interrupts When we get a performance monitor interrupt, we need to make sure that the host receives it. So reinject it like we reinject the other host destined interrupts. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/hw_irq.h | 1 + arch/powerpc/kvm/booke.c | 4 ++++ 2 files changed, 5 insertions(+) (limited to 'arch') diff --git a/arch/powerpc/include/asm/hw_irq.h b/arch/powerpc/include/asm/hw_irq.h index 51010bfc792e..c9aac24b02e2 100644 --- a/arch/powerpc/include/asm/hw_irq.h +++ b/arch/powerpc/include/asm/hw_irq.h @@ -33,6 +33,7 @@ extern void __replay_interrupt(unsigned int vector); extern void timer_interrupt(struct pt_regs *); +extern void performance_monitor_exception(struct pt_regs *regs); #ifdef CONFIG_PPC64 #include diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 488936bece69..8e8aa4cb5968 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -634,6 +634,10 @@ static void kvmppc_restart_interrupt(struct kvm_vcpu *vcpu, case BOOKE_INTERRUPT_MACHINE_CHECK: /* FIXME */ break; + case BOOKE_INTERRUPT_PERFORMANCE_MONITOR: + kvmppc_fill_pt_regs(®s); + performance_monitor_exception(®s); + break; } } -- cgit v1.2.3 From 03660ba27020250eae0b5a2722e0c7bec4968c3c Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 28 Feb 2012 12:00:41 +0100 Subject: KVM: PPC: Booke: only prepare to enter when we enter So far, we've always called prepare_to_enter even when all we did was return to the host. This patch changes that semantic to only call prepare_to_enter when we actually want to get back into the guest. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/booke.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 8e8aa4cb5968..9f27258d8035 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -464,7 +464,7 @@ int kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu) * * returns !0 if a signal is pending and check_signal is true */ -static int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu, bool check_signal) +static int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu) { int r = 0; @@ -477,7 +477,7 @@ static int kvmppc_prepare_to_enter(struct kvm_vcpu *vcpu, bool check_signal) continue; } - if (check_signal && signal_pending(current)) { + if (signal_pending(current)) { r = 1; break; } @@ -509,7 +509,7 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) } local_irq_disable(); - if (kvmppc_prepare_to_enter(vcpu, true)) { + if (kvmppc_prepare_to_enter(vcpu)) { kvm_run->exit_reason = KVM_EXIT_INTR; ret = -EINTR; goto out; @@ -946,11 +946,13 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, * To avoid clobbering exit_reason, only check for signals if we * aren't already exiting to userspace for some other reason. */ - local_irq_disable(); - if (kvmppc_prepare_to_enter(vcpu, !(r & RESUME_HOST))) { - run->exit_reason = KVM_EXIT_INTR; - r = (-EINTR << 2) | RESUME_HOST | (r & RESUME_FLAG_NV); - kvmppc_account_exit(vcpu, SIGNAL_EXITS); + if (!(r & RESUME_HOST)) { + local_irq_disable(); + if (kvmppc_prepare_to_enter(vcpu)) { + run->exit_reason = KVM_EXIT_INTR; + r = (-EINTR << 2) | RESUME_HOST | (r & RESUME_FLAG_NV); + kvmppc_account_exit(vcpu, SIGNAL_EXITS); + } } return r; -- cgit v1.2.3 From 3aaefef200f618dc455cdf18053a7aeb262b5a11 Mon Sep 17 00:00:00 2001 From: Matt Evans Date: Mon, 30 Jan 2012 20:25:31 +0000 Subject: KVM: PPC: Book3s: PR: Add SPAPR H_BULK_REMOVE support SPAPR support includes various in-kernel hypercalls, improving performance by cutting out the exit to userspace. H_BULK_REMOVE is implemented in this patch. Signed-off-by: Matt Evans Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/book3s_pr_papr.c | 82 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 4 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/book3s_pr_papr.c b/arch/powerpc/kvm/book3s_pr_papr.c index b9589324797b..6d1bfe246f0a 100644 --- a/arch/powerpc/kvm/book3s_pr_papr.c +++ b/arch/powerpc/kvm/book3s_pr_papr.c @@ -98,6 +98,83 @@ static int kvmppc_h_pr_remove(struct kvm_vcpu *vcpu) return EMULATE_DONE; } +/* Request defs for kvmppc_h_pr_bulk_remove() */ +#define H_BULK_REMOVE_TYPE 0xc000000000000000ULL +#define H_BULK_REMOVE_REQUEST 0x4000000000000000ULL +#define H_BULK_REMOVE_RESPONSE 0x8000000000000000ULL +#define H_BULK_REMOVE_END 0xc000000000000000ULL +#define H_BULK_REMOVE_CODE 0x3000000000000000ULL +#define H_BULK_REMOVE_SUCCESS 0x0000000000000000ULL +#define H_BULK_REMOVE_NOT_FOUND 0x1000000000000000ULL +#define H_BULK_REMOVE_PARM 0x2000000000000000ULL +#define H_BULK_REMOVE_HW 0x3000000000000000ULL +#define H_BULK_REMOVE_RC 0x0c00000000000000ULL +#define H_BULK_REMOVE_FLAGS 0x0300000000000000ULL +#define H_BULK_REMOVE_ABSOLUTE 0x0000000000000000ULL +#define H_BULK_REMOVE_ANDCOND 0x0100000000000000ULL +#define H_BULK_REMOVE_AVPN 0x0200000000000000ULL +#define H_BULK_REMOVE_PTEX 0x00ffffffffffffffULL +#define H_BULK_REMOVE_MAX_BATCH 4 + +static int kvmppc_h_pr_bulk_remove(struct kvm_vcpu *vcpu) +{ + int i; + int paramnr = 4; + int ret = H_SUCCESS; + + for (i = 0; i < H_BULK_REMOVE_MAX_BATCH; i++) { + unsigned long tsh = kvmppc_get_gpr(vcpu, paramnr+(2*i)); + unsigned long tsl = kvmppc_get_gpr(vcpu, paramnr+(2*i)+1); + unsigned long pteg, rb, flags; + unsigned long pte[2]; + unsigned long v = 0; + + if ((tsh & H_BULK_REMOVE_TYPE) == H_BULK_REMOVE_END) { + break; /* Exit success */ + } else if ((tsh & H_BULK_REMOVE_TYPE) != + H_BULK_REMOVE_REQUEST) { + ret = H_PARAMETER; + break; /* Exit fail */ + } + + tsh &= H_BULK_REMOVE_PTEX | H_BULK_REMOVE_FLAGS; + tsh |= H_BULK_REMOVE_RESPONSE; + + if ((tsh & H_BULK_REMOVE_ANDCOND) && + (tsh & H_BULK_REMOVE_AVPN)) { + tsh |= H_BULK_REMOVE_PARM; + kvmppc_set_gpr(vcpu, paramnr+(2*i), tsh); + ret = H_PARAMETER; + break; /* Exit fail */ + } + + pteg = get_pteg_addr(vcpu, tsh & H_BULK_REMOVE_PTEX); + copy_from_user(pte, (void __user *)pteg, sizeof(pte)); + + /* tsl = AVPN */ + flags = (tsh & H_BULK_REMOVE_FLAGS) >> 26; + + if ((pte[0] & HPTE_V_VALID) == 0 || + ((flags & H_AVPN) && (pte[0] & ~0x7fUL) != tsl) || + ((flags & H_ANDCOND) && (pte[0] & tsl) != 0)) { + tsh |= H_BULK_REMOVE_NOT_FOUND; + } else { + /* Splat the pteg in (userland) hpt */ + copy_to_user((void __user *)pteg, &v, sizeof(v)); + + rb = compute_tlbie_rb(pte[0], pte[1], + tsh & H_BULK_REMOVE_PTEX); + vcpu->arch.mmu.tlbie(vcpu, rb, rb & 1 ? true : false); + tsh |= H_BULK_REMOVE_SUCCESS; + tsh |= (pte[1] & (HPTE_R_C | HPTE_R_R)) << 43; + } + kvmppc_set_gpr(vcpu, paramnr+(2*i), tsh); + } + kvmppc_set_gpr(vcpu, 3, ret); + + return EMULATE_DONE; +} + static int kvmppc_h_pr_protect(struct kvm_vcpu *vcpu) { unsigned long flags = kvmppc_get_gpr(vcpu, 4); @@ -144,10 +221,7 @@ int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd) case H_PROTECT: return kvmppc_h_pr_protect(vcpu); case H_BULK_REMOVE: - /* We just flush all PTEs, so user space can - handle the HPT modifications */ - kvmppc_mmu_pte_flush(vcpu, 0, 0); - break; + return kvmppc_h_pr_bulk_remove(vcpu); case H_CEDE: kvm_vcpu_block(vcpu); vcpu->stat.halt_wakeup++; -- cgit v1.2.3 From f6127716c346c73ab1513edee53231800188c5ba Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 5 Mar 2012 16:00:28 +0100 Subject: KVM: PPC: Save/Restore CR over vcpu_run On PPC, CR2-CR4 are nonvolatile, thus have to be saved across function calls. We didn't respect that for any architecture until Paul spotted it in his patch for Book3S-HV. This patch saves/restores CR for all KVM capable PPC hosts. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/bookehv_interrupts.S | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/bookehv_interrupts.S b/arch/powerpc/kvm/bookehv_interrupts.S index 2d1f56cad48c..57e2fa414444 100644 --- a/arch/powerpc/kvm/bookehv_interrupts.S +++ b/arch/powerpc/kvm/bookehv_interrupts.S @@ -49,7 +49,8 @@ * kernel with the -ffixed-r2 gcc option. */ #define HOST_R2 (3 * LONGBYTES) -#define HOST_NV_GPRS (4 * LONGBYTES) +#define HOST_CR (4 * LONGBYTES) +#define HOST_NV_GPRS (5 * LONGBYTES) #define HOST_NV_GPR(n) (HOST_NV_GPRS + ((n - 14) * LONGBYTES)) #define HOST_MIN_STACK_SIZE (HOST_NV_GPR(31) + LONGBYTES) #define HOST_STACK_SIZE ((HOST_MIN_STACK_SIZE + 15) & ~15) /* Align. */ @@ -396,6 +397,7 @@ skip_nv_load: heavyweight_exit: /* Not returning to guest. */ PPC_LL r5, HOST_STACK_LR(r1) + lwz r6, HOST_CR(r1) /* * We already saved guest volatile register state; now save the @@ -442,6 +444,7 @@ heavyweight_exit: /* Return to kvm_vcpu_run(). */ mtlr r5 + mtcr r6 addi r1, r1, HOST_STACK_SIZE /* r3 still contains the return code from kvmppc_handle_exit(). */ blr @@ -457,8 +460,11 @@ _GLOBAL(__kvmppc_vcpu_run) /* Save host state to stack. */ PPC_STL r3, HOST_RUN(r1) mflr r3 + mfcr r5 PPC_STL r3, HOST_STACK_LR(r1) + stw r5, HOST_CR(r1) + /* Save host non-volatile register state to stack. */ PPC_STL r14, HOST_NV_GPR(r14)(r1) PPC_STL r15, HOST_NV_GPR(r15)(r1) -- cgit v1.2.3 From f0888f70151c7f53de2b45ee20ff1905837943e8 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 3 Feb 2012 00:54:17 +0000 Subject: KVM: PPC: Book3S HV: Make secondary threads more robust against stray IPIs Currently on POWER7, if we are running the guest on a core and we don't need all the hardware threads, we do nothing to ensure that the unused threads aren't executing in the kernel (other than checking that they are offline). We just assume they're napping and we don't do anything to stop them trying to enter the kernel while the guest is running. This means that a stray IPI can wake up the hardware thread and it will then try to enter the kernel, but since the core is in guest context, it will execute code from the guest in hypervisor mode once it turns the MMU on, which tends to lead to crashes or hangs in the host. This fixes the problem by adding two new one-byte flags in the kvmppc_host_state structure in the PACA which are used to interlock between the primary thread and the unused secondary threads when entering the guest. With these flags, the primary thread can ensure that the unused secondaries are not already in kernel mode (i.e. handling a stray IPI) and then indicate that they should not try to enter the kernel if they do get woken for any reason. Instead they will go into KVM code, find that there is no vcpu to run, acknowledge and clear the IPI and go back to nap mode. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/kvm_book3s_asm.h | 8 +++ arch/powerpc/kernel/asm-offsets.c | 2 + arch/powerpc/kernel/exceptions-64s.S | 12 ++-- arch/powerpc/kernel/idle_power7.S | 7 +++ arch/powerpc/kvm/book3s_hv.c | 49 +++++++++++++++- arch/powerpc/kvm/book3s_hv_rmhandlers.S | 92 ++++++++++++++++++------------- 6 files changed, 124 insertions(+), 46 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/include/asm/kvm_book3s_asm.h b/arch/powerpc/include/asm/kvm_book3s_asm.h index 1f2f5b6156bd..88609b23b775 100644 --- a/arch/powerpc/include/asm/kvm_book3s_asm.h +++ b/arch/powerpc/include/asm/kvm_book3s_asm.h @@ -79,6 +79,9 @@ struct kvmppc_host_state { u8 napping; #ifdef CONFIG_KVM_BOOK3S_64_HV + u8 hwthread_req; + u8 hwthread_state; + struct kvm_vcpu *kvm_vcpu; struct kvmppc_vcore *kvm_vcore; unsigned long xics_phys; @@ -122,4 +125,9 @@ struct kvmppc_book3s_shadow_vcpu { #endif /*__ASSEMBLY__ */ +/* Values for kvm_state */ +#define KVM_HWTHREAD_IN_KERNEL 0 +#define KVM_HWTHREAD_IN_NAP 1 +#define KVM_HWTHREAD_IN_KVM 2 + #endif /* __ASM_KVM_BOOK3S_ASM_H__ */ diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index bbede5882c5b..2abcf7d4b29c 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -540,6 +540,8 @@ int main(void) HSTATE_FIELD(HSTATE_IN_GUEST, in_guest); HSTATE_FIELD(HSTATE_RESTORE_HID5, restore_hid5); HSTATE_FIELD(HSTATE_NAPPING, napping); + HSTATE_FIELD(HSTATE_HWTHREAD_REQ, hwthread_req); + HSTATE_FIELD(HSTATE_HWTHREAD_STATE, hwthread_state); #ifdef CONFIG_KVM_BOOK3S_64_HV HSTATE_FIELD(HSTATE_KVM_VCPU, kvm_vcpu); diff --git a/arch/powerpc/kernel/exceptions-64s.S b/arch/powerpc/kernel/exceptions-64s.S index cb705fdbb458..8829b1095f7f 100644 --- a/arch/powerpc/kernel/exceptions-64s.S +++ b/arch/powerpc/kernel/exceptions-64s.S @@ -63,11 +63,13 @@ BEGIN_FTR_SECTION GET_PACA(r13) #ifdef CONFIG_KVM_BOOK3S_64_HV - lbz r0,PACAPROCSTART(r13) - cmpwi r0,0x80 - bne 1f - li r0,1 - stb r0,PACAPROCSTART(r13) + li r0,KVM_HWTHREAD_IN_KERNEL + stb r0,HSTATE_HWTHREAD_STATE(r13) + /* Order setting hwthread_state vs. testing hwthread_req */ + sync + lbz r0,HSTATE_HWTHREAD_REQ(r13) + cmpwi r0,0 + beq 1f b kvm_start_guest 1: #endif diff --git a/arch/powerpc/kernel/idle_power7.S b/arch/powerpc/kernel/idle_power7.S index 0cdc9a392839..7140d838339e 100644 --- a/arch/powerpc/kernel/idle_power7.S +++ b/arch/powerpc/kernel/idle_power7.S @@ -16,6 +16,7 @@ #include #include #include +#include #undef DEBUG @@ -81,6 +82,12 @@ _GLOBAL(power7_idle) std r9,_MSR(r1) std r1,PACAR1(r13) +#ifdef CONFIG_KVM_BOOK3S_64_HV + /* Tell KVM we're napping */ + li r4,KVM_HWTHREAD_IN_NAP + stb r4,HSTATE_HWTHREAD_STATE(r13) +#endif + /* Magic NAP mode enter sequence */ std r0,0(r1) ptesync diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 01294a5099dd..e87f6196d222 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -569,6 +569,45 @@ static void kvmppc_remove_runnable(struct kvmppc_vcore *vc, list_del(&vcpu->arch.run_list); } +static int kvmppc_grab_hwthread(int cpu) +{ + struct paca_struct *tpaca; + long timeout = 1000; + + tpaca = &paca[cpu]; + + /* Ensure the thread won't go into the kernel if it wakes */ + tpaca->kvm_hstate.hwthread_req = 1; + + /* + * If the thread is already executing in the kernel (e.g. handling + * a stray interrupt), wait for it to get back to nap mode. + * The smp_mb() is to ensure that our setting of hwthread_req + * is visible before we look at hwthread_state, so if this + * races with the code at system_reset_pSeries and the thread + * misses our setting of hwthread_req, we are sure to see its + * setting of hwthread_state, and vice versa. + */ + smp_mb(); + while (tpaca->kvm_hstate.hwthread_state == KVM_HWTHREAD_IN_KERNEL) { + if (--timeout <= 0) { + pr_err("KVM: couldn't grab cpu %d\n", cpu); + return -EBUSY; + } + udelay(1); + } + return 0; +} + +static void kvmppc_release_hwthread(int cpu) +{ + struct paca_struct *tpaca; + + tpaca = &paca[cpu]; + tpaca->kvm_hstate.hwthread_req = 0; + tpaca->kvm_hstate.kvm_vcpu = NULL; +} + static void kvmppc_start_thread(struct kvm_vcpu *vcpu) { int cpu; @@ -588,8 +627,7 @@ static void kvmppc_start_thread(struct kvm_vcpu *vcpu) smp_wmb(); #if defined(CONFIG_PPC_ICP_NATIVE) && defined(CONFIG_SMP) if (vcpu->arch.ptid) { - tpaca->cpu_start = 0x80; - wmb(); + kvmppc_grab_hwthread(cpu); xics_wake_cpu(cpu); ++vc->n_woken; } @@ -639,7 +677,7 @@ static int kvmppc_run_core(struct kvmppc_vcore *vc) struct kvm_vcpu *vcpu, *vcpu0, *vnext; long ret; u64 now; - int ptid; + int ptid, i; /* don't start if any threads have a signal pending */ list_for_each_entry(vcpu, &vc->runnable_threads, arch.run_list) @@ -686,12 +724,17 @@ static int kvmppc_run_core(struct kvmppc_vcore *vc) vc->napping_threads = 0; list_for_each_entry(vcpu, &vc->runnable_threads, arch.run_list) kvmppc_start_thread(vcpu); + /* Grab any remaining hw threads so they can't go into the kernel */ + for (i = ptid; i < threads_per_core; ++i) + kvmppc_grab_hwthread(vc->pcpu + i); preempt_disable(); spin_unlock(&vc->lock); kvm_guest_enter(); __kvmppc_vcore_entry(NULL, vcpu0); + for (i = 0; i < threads_per_core; ++i) + kvmppc_release_hwthread(vc->pcpu + i); spin_lock(&vc->lock); /* disable sending of IPIs on virtual external irqs */ diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index b70bf22a3ff3..d595033bd449 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -26,6 +26,7 @@ #include #include #include +#include /***************************************************************************** * * @@ -82,6 +83,7 @@ _GLOBAL(kvmppc_hv_entry_trampoline) #define XICS_XIRR 4 #define XICS_QIRR 0xc +#define XICS_IPI 2 /* interrupt source # for IPIs */ /* * We come in here when wakened from nap mode on a secondary hw thread. @@ -94,26 +96,54 @@ kvm_start_guest: subi r1,r1,STACK_FRAME_OVERHEAD ld r2,PACATOC(r13) - /* were we napping due to cede? */ - lbz r0,HSTATE_NAPPING(r13) - cmpwi r0,0 - bne kvm_end_cede + li r0,KVM_HWTHREAD_IN_KVM + stb r0,HSTATE_HWTHREAD_STATE(r13) - /* get vcpu pointer */ - ld r4, HSTATE_KVM_VCPU(r13) + /* NV GPR values from power7_idle() will no longer be valid */ + li r0,1 + stb r0,PACA_NAPSTATELOST(r13) - /* We got here with an IPI; clear it */ - ld r5, HSTATE_XICS_PHYS(r13) - li r0, 0xff - li r6, XICS_QIRR - li r7, XICS_XIRR - lwzcix r8, r5, r7 /* ack the interrupt */ + /* get vcpu pointer, NULL if we have no vcpu to run */ + ld r4,HSTATE_KVM_VCPU(r13) + cmpdi cr1,r4,0 + + /* Check the wake reason in SRR1 to see why we got here */ + mfspr r3,SPRN_SRR1 + rlwinm r3,r3,44-31,0x7 /* extract wake reason field */ + cmpwi r3,4 /* was it an external interrupt? */ + bne 27f + + /* + * External interrupt - for now assume it is an IPI, since we + * should never get any other interrupts sent to offline threads. + * Only do this for secondary threads. + */ + beq cr1,25f + lwz r3,VCPU_PTID(r4) + cmpwi r3,0 + beq 27f +25: ld r5,HSTATE_XICS_PHYS(r13) + li r0,0xff + li r6,XICS_QIRR + li r7,XICS_XIRR + lwzcix r8,r5,r7 /* get and ack the interrupt */ sync - stbcix r0, r5, r6 /* clear it */ - stwcix r8, r5, r7 /* EOI it */ + clrldi. r9,r8,40 /* get interrupt source ID. */ + beq 27f /* none there? */ + cmpwi r9,XICS_IPI + bne 26f + stbcix r0,r5,r6 /* clear IPI */ +26: stwcix r8,r5,r7 /* EOI the interrupt */ - /* NV GPR values from power7_idle() will no longer be valid */ - stb r0, PACA_NAPSTATELOST(r13) +27: /* XXX should handle hypervisor maintenance interrupts etc. here */ + + /* if we have no vcpu to run, go back to sleep */ + beq cr1,kvm_no_guest + + /* were we napping due to cede? */ + lbz r0,HSTATE_NAPPING(r13) + cmpwi r0,0 + bne kvm_end_cede .global kvmppc_hv_entry kvmppc_hv_entry: @@ -1445,8 +1475,8 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206) * Take a nap until a decrementer or external interrupt occurs, * with PECE1 (wake on decr) and PECE0 (wake on external) set in LPCR */ - li r0,0x80 - stb r0,PACAPROCSTART(r13) + li r0,1 + stb r0,HSTATE_HWTHREAD_REQ(r13) mfspr r5,SPRN_LPCR ori r5,r5,LPCR_PECE0 | LPCR_PECE1 mtspr SPRN_LPCR,r5 @@ -1463,26 +1493,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_206) kvm_end_cede: /* Woken by external or decrementer interrupt */ ld r1, HSTATE_HOST_R1(r13) - ld r2, PACATOC(r13) - /* If we're a secondary thread and we got here by an IPI, ack it */ - ld r4,HSTATE_KVM_VCPU(r13) - lwz r3,VCPU_PTID(r4) - cmpwi r3,0 - beq 27f - mfspr r3,SPRN_SRR1 - rlwinm r3,r3,44-31,0x7 /* extract wake reason field */ - cmpwi r3,4 /* was it an external interrupt? */ - bne 27f - ld r5, HSTATE_XICS_PHYS(r13) - li r0,0xff - li r6,XICS_QIRR - li r7,XICS_XIRR - lwzcix r8,r5,r7 /* ack the interrupt */ - sync - stbcix r0,r5,r6 /* clear it */ - stwcix r8,r5,r7 /* EOI it */ -27: /* load up FP state */ bl kvmppc_load_fp @@ -1580,12 +1591,17 @@ secondary_nap: stwcx. r3, 0, r4 bne 51b +kvm_no_guest: + li r0, KVM_HWTHREAD_IN_NAP + stb r0, HSTATE_HWTHREAD_STATE(r13) + li r0, 0 + std r0, HSTATE_KVM_VCPU(r13) + li r3, LPCR_PECE0 mfspr r4, SPRN_LPCR rlwimi r4, r3, 0, LPCR_PECE0 | LPCR_PECE1 mtspr SPRN_LPCR, r4 isync - li r0, 0 std r0, HSTATE_SCRATCH0(r13) ptesync ld r0, HSTATE_SCRATCH0(r13) -- cgit v1.2.3 From 2e25aa5f64b18a97f35266e51c71ff4dc644db0c Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Sun, 19 Feb 2012 17:46:32 +0000 Subject: KVM: PPC: Book3S HV: Make virtual processor area registration more robust The PAPR API allows three sorts of per-virtual-processor areas to be registered (VPA, SLB shadow buffer, and dispatch trace log), and furthermore, these can be registered and unregistered for another virtual CPU. Currently we just update the vcpu fields pointing to these areas at the time of registration or unregistration. If this is done on another vcpu, there is the possibility that the target vcpu is using those fields at the time and could end up using a bogus pointer and corrupting memory. This fixes the race by making the target cpu itself do the update, so we can be sure that the update happens at a time when the fields aren't being used. Each area now has a struct kvmppc_vpa which is used to manage these updates. There is also a spinlock which protects access to all of the kvmppc_vpa structs, other than to the pinned_addr fields. (We could have just taken the spinlock when using the vpa, slb_shadow or dtl fields, but that would mean taking the spinlock on every guest entry and exit.) This also changes 'struct dtl' (which was undefined) to 'struct dtl_entry', which is what the rest of the kernel uses. Thanks to Michael Ellerman for pointing out the need to initialize vcpu->arch.vpa_update_lock. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/hvcall.h | 10 ++ arch/powerpc/include/asm/kvm_host.h | 27 ++++- arch/powerpc/kernel/asm-offsets.c | 2 +- arch/powerpc/kvm/book3s_hv.c | 227 +++++++++++++++++++++++++----------- 4 files changed, 190 insertions(+), 76 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h index 1c324ff55ea8..318bac9f8752 100644 --- a/arch/powerpc/include/asm/hvcall.h +++ b/arch/powerpc/include/asm/hvcall.h @@ -114,6 +114,16 @@ #define H_PP1 (1UL<<(63-62)) #define H_PP2 (1UL<<(63-63)) +/* Flags for H_REGISTER_VPA subfunction field */ +#define H_VPA_FUNC_SHIFT (63-18) /* Bit posn of subfunction code */ +#define H_VPA_FUNC_MASK 7UL +#define H_VPA_REG_VPA 1UL /* Register Virtual Processor Area */ +#define H_VPA_REG_DTL 2UL /* Register Dispatch Trace Log */ +#define H_VPA_REG_SLB 3UL /* Register SLB shadow buffer */ +#define H_VPA_DEREG_VPA 5UL /* Deregister Virtual Processor Area */ +#define H_VPA_DEREG_DTL 6UL /* Deregister Dispatch Trace Log */ +#define H_VPA_DEREG_SLB 7UL /* Deregister SLB shadow buffer */ + /* VASI States */ #define H_VASI_INVALID 0 #define H_VASI_ENABLED 1 diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 97ecdaf82956..93ffd8dd8554 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -82,7 +82,7 @@ struct kvm_vcpu; struct lppaca; struct slb_shadow; -struct dtl; +struct dtl_entry; struct kvm_vm_stat { u32 remote_tlb_flush; @@ -279,6 +279,19 @@ struct kvmppc_vcore { #define VCORE_EXITING 2 #define VCORE_SLEEPING 3 +/* + * Struct used to manage memory for a virtual processor area + * registered by a PAPR guest. There are three types of area + * that a guest can register. + */ +struct kvmppc_vpa { + void *pinned_addr; /* Address in kernel linear mapping */ + void *pinned_end; /* End of region */ + unsigned long next_gpa; /* Guest phys addr for update */ + unsigned long len; /* Number of bytes required */ + u8 update_pending; /* 1 => update pinned_addr from next_gpa */ +}; + struct kvmppc_pte { ulong eaddr; u64 vpage; @@ -473,11 +486,6 @@ struct kvm_vcpu_arch { u8 prodded; u32 last_inst; - struct lppaca *vpa; - struct slb_shadow *slb_shadow; - struct dtl *dtl; - struct dtl *dtl_end; - wait_queue_head_t *wqp; struct kvmppc_vcore *vcore; int ret; @@ -502,6 +510,13 @@ struct kvm_vcpu_arch { struct task_struct *run_task; struct kvm_run *kvm_run; pgd_t *pgdir; + + spinlock_t vpa_update_lock; + struct kvmppc_vpa vpa; + struct kvmppc_vpa dtl; + struct dtl_entry *dtl_ptr; + unsigned long dtl_index; + struct kvmppc_vpa slb_shadow; #endif }; diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 2abcf7d4b29c..502e038f8c8a 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -466,7 +466,7 @@ int main(void) DEFINE(VCPU_PENDING_EXC, offsetof(struct kvm_vcpu, arch.pending_exceptions)); DEFINE(VCPU_CEDED, offsetof(struct kvm_vcpu, arch.ceded)); DEFINE(VCPU_PRODDED, offsetof(struct kvm_vcpu, arch.prodded)); - DEFINE(VCPU_VPA, offsetof(struct kvm_vcpu, arch.vpa)); + DEFINE(VCPU_VPA, offsetof(struct kvm_vcpu, arch.vpa.pinned_addr)); DEFINE(VCPU_MMCR, offsetof(struct kvm_vcpu, arch.mmcr)); DEFINE(VCPU_PMC, offsetof(struct kvm_vcpu, arch.pmc)); DEFINE(VCPU_SLB, offsetof(struct kvm_vcpu, arch.slb)); diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index e87f6196d222..2444a9ce781f 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -134,6 +134,22 @@ static void init_vpa(struct kvm_vcpu *vcpu, struct lppaca *vpa) vpa->yield_count = 1; } +/* Length for a per-processor buffer is passed in at offset 4 in the buffer */ +struct reg_vpa { + u32 dummy; + union { + u16 hword; + u32 word; + } length; +}; + +static int vpa_is_registered(struct kvmppc_vpa *vpap) +{ + if (vpap->update_pending) + return vpap->next_gpa != 0; + return vpap->pinned_addr != NULL; +} + static unsigned long do_h_register_vpa(struct kvm_vcpu *vcpu, unsigned long flags, unsigned long vcpuid, unsigned long vpa) @@ -142,88 +158,153 @@ static unsigned long do_h_register_vpa(struct kvm_vcpu *vcpu, unsigned long len, nb; void *va; struct kvm_vcpu *tvcpu; - int err = H_PARAMETER; + int err; + int subfunc; + struct kvmppc_vpa *vpap; tvcpu = kvmppc_find_vcpu(kvm, vcpuid); if (!tvcpu) return H_PARAMETER; - flags >>= 63 - 18; - flags &= 7; - if (flags == 0 || flags == 4) - return H_PARAMETER; - if (flags < 4) { - if (vpa & 0x7f) + subfunc = (flags >> H_VPA_FUNC_SHIFT) & H_VPA_FUNC_MASK; + if (subfunc == H_VPA_REG_VPA || subfunc == H_VPA_REG_DTL || + subfunc == H_VPA_REG_SLB) { + /* Registering new area - address must be cache-line aligned */ + if ((vpa & (L1_CACHE_BYTES - 1)) || !vpa) return H_PARAMETER; - if (flags >= 2 && !tvcpu->arch.vpa) - return H_RESOURCE; - /* registering new area; convert logical addr to real */ + + /* convert logical addr to kernel addr and read length */ va = kvmppc_pin_guest_page(kvm, vpa, &nb); if (va == NULL) return H_PARAMETER; - if (flags <= 1) - len = *(unsigned short *)(va + 4); + if (subfunc == H_VPA_REG_VPA) + len = ((struct reg_vpa *)va)->length.hword; else - len = *(unsigned int *)(va + 4); - if (len > nb) - goto out_unpin; - switch (flags) { - case 1: /* register VPA */ - if (len < 640) - goto out_unpin; - if (tvcpu->arch.vpa) - kvmppc_unpin_guest_page(kvm, vcpu->arch.vpa); - tvcpu->arch.vpa = va; - init_vpa(vcpu, va); - break; - case 2: /* register DTL */ - if (len < 48) - goto out_unpin; - len -= len % 48; - if (tvcpu->arch.dtl) - kvmppc_unpin_guest_page(kvm, vcpu->arch.dtl); - tvcpu->arch.dtl = va; - tvcpu->arch.dtl_end = va + len; + len = ((struct reg_vpa *)va)->length.word; + kvmppc_unpin_guest_page(kvm, va); + + /* Check length */ + if (len > nb || len < sizeof(struct reg_vpa)) + return H_PARAMETER; + } else { + vpa = 0; + len = 0; + } + + err = H_PARAMETER; + vpap = NULL; + spin_lock(&tvcpu->arch.vpa_update_lock); + + switch (subfunc) { + case H_VPA_REG_VPA: /* register VPA */ + if (len < sizeof(struct lppaca)) break; - case 3: /* register SLB shadow buffer */ - if (len < 16) - goto out_unpin; - if (tvcpu->arch.slb_shadow) - kvmppc_unpin_guest_page(kvm, vcpu->arch.slb_shadow); - tvcpu->arch.slb_shadow = va; + vpap = &tvcpu->arch.vpa; + err = 0; + break; + + case H_VPA_REG_DTL: /* register DTL */ + if (len < sizeof(struct dtl_entry)) break; - } - } else { - switch (flags) { - case 5: /* unregister VPA */ - if (tvcpu->arch.slb_shadow || tvcpu->arch.dtl) - return H_RESOURCE; - if (!tvcpu->arch.vpa) - break; - kvmppc_unpin_guest_page(kvm, tvcpu->arch.vpa); - tvcpu->arch.vpa = NULL; + len -= len % sizeof(struct dtl_entry); + + /* Check that they have previously registered a VPA */ + err = H_RESOURCE; + if (!vpa_is_registered(&tvcpu->arch.vpa)) break; - case 6: /* unregister DTL */ - if (!tvcpu->arch.dtl) - break; - kvmppc_unpin_guest_page(kvm, tvcpu->arch.dtl); - tvcpu->arch.dtl = NULL; + + vpap = &tvcpu->arch.dtl; + err = 0; + break; + + case H_VPA_REG_SLB: /* register SLB shadow buffer */ + /* Check that they have previously registered a VPA */ + err = H_RESOURCE; + if (!vpa_is_registered(&tvcpu->arch.vpa)) break; - case 7: /* unregister SLB shadow buffer */ - if (!tvcpu->arch.slb_shadow) - break; - kvmppc_unpin_guest_page(kvm, tvcpu->arch.slb_shadow); - tvcpu->arch.slb_shadow = NULL; + + vpap = &tvcpu->arch.slb_shadow; + err = 0; + break; + + case H_VPA_DEREG_VPA: /* deregister VPA */ + /* Check they don't still have a DTL or SLB buf registered */ + err = H_RESOURCE; + if (vpa_is_registered(&tvcpu->arch.dtl) || + vpa_is_registered(&tvcpu->arch.slb_shadow)) break; - } + + vpap = &tvcpu->arch.vpa; + err = 0; + break; + + case H_VPA_DEREG_DTL: /* deregister DTL */ + vpap = &tvcpu->arch.dtl; + err = 0; + break; + + case H_VPA_DEREG_SLB: /* deregister SLB shadow buffer */ + vpap = &tvcpu->arch.slb_shadow; + err = 0; + break; } - return H_SUCCESS; - out_unpin: - kvmppc_unpin_guest_page(kvm, va); + if (vpap) { + vpap->next_gpa = vpa; + vpap->len = len; + vpap->update_pending = 1; + } + + spin_unlock(&tvcpu->arch.vpa_update_lock); + return err; } +static void kvmppc_update_vpa(struct kvm *kvm, struct kvmppc_vpa *vpap) +{ + void *va; + unsigned long nb; + + vpap->update_pending = 0; + va = NULL; + if (vpap->next_gpa) { + va = kvmppc_pin_guest_page(kvm, vpap->next_gpa, &nb); + if (nb < vpap->len) { + /* + * If it's now too short, it must be that userspace + * has changed the mappings underlying guest memory, + * so unregister the region. + */ + kvmppc_unpin_guest_page(kvm, va); + va = NULL; + } + } + if (vpap->pinned_addr) + kvmppc_unpin_guest_page(kvm, vpap->pinned_addr); + vpap->pinned_addr = va; + if (va) + vpap->pinned_end = va + vpap->len; +} + +static void kvmppc_update_vpas(struct kvm_vcpu *vcpu) +{ + struct kvm *kvm = vcpu->kvm; + + spin_lock(&vcpu->arch.vpa_update_lock); + if (vcpu->arch.vpa.update_pending) { + kvmppc_update_vpa(kvm, &vcpu->arch.vpa); + init_vpa(vcpu, vcpu->arch.vpa.pinned_addr); + } + if (vcpu->arch.dtl.update_pending) { + kvmppc_update_vpa(kvm, &vcpu->arch.dtl); + vcpu->arch.dtl_ptr = vcpu->arch.dtl.pinned_addr; + vcpu->arch.dtl_index = 0; + } + if (vcpu->arch.slb_shadow.update_pending) + kvmppc_update_vpa(kvm, &vcpu->arch.slb_shadow); + spin_unlock(&vcpu->arch.vpa_update_lock); +} + int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu) { unsigned long req = kvmppc_get_gpr(vcpu, 3); @@ -468,6 +549,7 @@ struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id) /* default to host PVR, since we can't spoof it */ vcpu->arch.pvr = mfspr(SPRN_PVR); kvmppc_set_pvr(vcpu, vcpu->arch.pvr); + spin_lock_init(&vcpu->arch.vpa_update_lock); kvmppc_mmu_book3s_hv_init(vcpu); @@ -512,12 +594,14 @@ out: void kvmppc_core_vcpu_free(struct kvm_vcpu *vcpu) { - if (vcpu->arch.dtl) - kvmppc_unpin_guest_page(vcpu->kvm, vcpu->arch.dtl); - if (vcpu->arch.slb_shadow) - kvmppc_unpin_guest_page(vcpu->kvm, vcpu->arch.slb_shadow); - if (vcpu->arch.vpa) - kvmppc_unpin_guest_page(vcpu->kvm, vcpu->arch.vpa); + spin_lock(&vcpu->arch.vpa_update_lock); + if (vcpu->arch.dtl.pinned_addr) + kvmppc_unpin_guest_page(vcpu->kvm, vcpu->arch.dtl.pinned_addr); + if (vcpu->arch.slb_shadow.pinned_addr) + kvmppc_unpin_guest_page(vcpu->kvm, vcpu->arch.slb_shadow.pinned_addr); + if (vcpu->arch.vpa.pinned_addr) + kvmppc_unpin_guest_page(vcpu->kvm, vcpu->arch.vpa.pinned_addr); + spin_unlock(&vcpu->arch.vpa_update_lock); kvm_vcpu_uninit(vcpu); kmem_cache_free(kvm_vcpu_cache, vcpu); } @@ -722,8 +806,13 @@ static int kvmppc_run_core(struct kvmppc_vcore *vc) vc->in_guest = 0; vc->pcpu = smp_processor_id(); vc->napping_threads = 0; - list_for_each_entry(vcpu, &vc->runnable_threads, arch.run_list) + list_for_each_entry(vcpu, &vc->runnable_threads, arch.run_list) { kvmppc_start_thread(vcpu); + if (vcpu->arch.vpa.update_pending || + vcpu->arch.slb_shadow.update_pending || + vcpu->arch.dtl.update_pending) + kvmppc_update_vpas(vcpu); + } /* Grab any remaining hw threads so they can't go into the kernel */ for (i = ptid; i < threads_per_core; ++i) kvmppc_grab_hwthread(vc->pcpu + i); -- cgit v1.2.3 From 0456ec4ff2b832ab9ff476ed687fea704500f1cd Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 3 Feb 2012 00:56:21 +0000 Subject: KVM: PPC: Book3S HV: Report stolen time to guest through dispatch trace log This adds code to measure "stolen" time per virtual core in units of timebase ticks, and to report the stolen time to the guest using the dispatch trace log (DTL). The guest can register an area of memory for the DTL for a given vcpu. The DTL is a ring buffer where KVM fills in one entry every time it enters the guest for that vcpu. Stolen time is measured as time when the virtual core is not running, either because the vcore is not runnable (e.g. some of its vcpus are executing elsewhere in the kernel or in userspace), or when the vcpu thread that is running the vcore is preempted. This includes time when all the vcpus are idle (i.e. have executed the H_CEDE hypercall), which is OK because the guest accounts stolen time while idle as idle time. Each vcpu keeps a record of how much stolen time has been reported to the guest for that vcpu so far. When we are about to enter the guest, we create a new DTL entry (if the guest vcpu has a DTL) and report the difference between total stolen time for the vcore and stolen time reported so far for the vcpu as the "enqueue to dispatch" time in the DTL entry. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/kvm_host.h | 4 ++++ arch/powerpc/kvm/book3s_hv.c | 46 ++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 93ffd8dd8554..014eaf27a239 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -268,6 +268,9 @@ struct kvmppc_vcore { struct list_head runnable_threads; spinlock_t lock; wait_queue_head_t wq; + u64 stolen_tb; + u64 preempt_tb; + struct kvm_vcpu *runner; }; #define VCORE_ENTRY_COUNT(vc) ((vc)->entry_exit_count & 0xff) @@ -516,6 +519,7 @@ struct kvm_vcpu_arch { struct kvmppc_vpa dtl; struct dtl_entry *dtl_ptr; unsigned long dtl_index; + u64 stolen_logged; struct kvmppc_vpa slb_shadow; #endif }; diff --git a/arch/powerpc/kvm/book3s_hv.c b/arch/powerpc/kvm/book3s_hv.c index 2444a9ce781f..907935764de0 100644 --- a/arch/powerpc/kvm/book3s_hv.c +++ b/arch/powerpc/kvm/book3s_hv.c @@ -60,12 +60,20 @@ static int kvmppc_hv_setup_rma(struct kvm_vcpu *vcpu); void kvmppc_core_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { + struct kvmppc_vcore *vc = vcpu->arch.vcore; + local_paca->kvm_hstate.kvm_vcpu = vcpu; - local_paca->kvm_hstate.kvm_vcore = vcpu->arch.vcore; + local_paca->kvm_hstate.kvm_vcore = vc; + if (vc->runner == vcpu && vc->vcore_state != VCORE_INACTIVE) + vc->stolen_tb += mftb() - vc->preempt_tb; } void kvmppc_core_vcpu_put(struct kvm_vcpu *vcpu) { + struct kvmppc_vcore *vc = vcpu->arch.vcore; + + if (vc->runner == vcpu && vc->vcore_state != VCORE_INACTIVE) + vc->preempt_tb = mftb(); } void kvmppc_set_msr(struct kvm_vcpu *vcpu, u64 msr) @@ -305,6 +313,35 @@ static void kvmppc_update_vpas(struct kvm_vcpu *vcpu) spin_unlock(&vcpu->arch.vpa_update_lock); } +static void kvmppc_create_dtl_entry(struct kvm_vcpu *vcpu, + struct kvmppc_vcore *vc) +{ + struct dtl_entry *dt; + struct lppaca *vpa; + unsigned long old_stolen; + + dt = vcpu->arch.dtl_ptr; + vpa = vcpu->arch.vpa.pinned_addr; + old_stolen = vcpu->arch.stolen_logged; + vcpu->arch.stolen_logged = vc->stolen_tb; + if (!dt || !vpa) + return; + memset(dt, 0, sizeof(struct dtl_entry)); + dt->dispatch_reason = 7; + dt->processor_id = vc->pcpu + vcpu->arch.ptid; + dt->timebase = mftb(); + dt->enqueue_to_dispatch_time = vc->stolen_tb - old_stolen; + dt->srr0 = kvmppc_get_pc(vcpu); + dt->srr1 = vcpu->arch.shregs.msr; + ++dt; + if (dt == vcpu->arch.dtl.pinned_end) + dt = vcpu->arch.dtl.pinned_addr; + vcpu->arch.dtl_ptr = dt; + /* order writing *dt vs. writing vpa->dtl_idx */ + smp_wmb(); + vpa->dtl_idx = ++vcpu->arch.dtl_index; +} + int kvmppc_pseries_do_hcall(struct kvm_vcpu *vcpu) { unsigned long req = kvmppc_get_gpr(vcpu, 3); @@ -568,6 +605,7 @@ struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id) INIT_LIST_HEAD(&vcore->runnable_threads); spin_lock_init(&vcore->lock); init_waitqueue_head(&vcore->wq); + vcore->preempt_tb = mftb(); } kvm->arch.vcores[core] = vcore; } @@ -580,6 +618,7 @@ struct kvm_vcpu *kvmppc_core_vcpu_create(struct kvm *kvm, unsigned int id) ++vcore->num_threads; spin_unlock(&vcore->lock); vcpu->arch.vcore = vcore; + vcpu->arch.stolen_logged = vcore->stolen_tb; vcpu->arch.cpu_type = KVM_CPU_3S_64; kvmppc_sanity_check(vcpu); @@ -803,6 +842,7 @@ static int kvmppc_run_core(struct kvmppc_vcore *vc) vc->nap_count = 0; vc->entry_exit_count = 0; vc->vcore_state = VCORE_RUNNING; + vc->stolen_tb += mftb() - vc->preempt_tb; vc->in_guest = 0; vc->pcpu = smp_processor_id(); vc->napping_threads = 0; @@ -812,6 +852,7 @@ static int kvmppc_run_core(struct kvmppc_vcore *vc) vcpu->arch.slb_shadow.update_pending || vcpu->arch.dtl.update_pending) kvmppc_update_vpas(vcpu); + kvmppc_create_dtl_entry(vcpu, vc); } /* Grab any remaining hw threads so they can't go into the kernel */ for (i = ptid; i < threads_per_core; ++i) @@ -869,6 +910,7 @@ static int kvmppc_run_core(struct kvmppc_vcore *vc) spin_lock(&vc->lock); out: vc->vcore_state = VCORE_INACTIVE; + vc->preempt_tb = mftb(); list_for_each_entry_safe(vcpu, vnext, &vc->runnable_threads, arch.run_list) { if (vcpu->arch.ret != RESUME_GUEST) { @@ -967,6 +1009,7 @@ static int kvmppc_run_vcpu(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) spin_lock(&vc->lock); continue; } + vc->runner = vcpu; n_ceded = 0; list_for_each_entry(v, &vc->runnable_threads, arch.run_list) n_ceded += v->arch.ceded; @@ -986,6 +1029,7 @@ static int kvmppc_run_vcpu(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) wake_up(&v->arch.cpu_run); } } + vc->runner = NULL; } if (signal_pending(current)) { -- cgit v1.2.3 From c0fe7b099931c6c05c98a05c277185ee25254f35 Mon Sep 17 00:00:00 2001 From: Bharat Bhushan Date: Mon, 5 Mar 2012 01:34:08 +0000 Subject: Restore guest CR after exit timing calculation No instruction which can change Condition Register (CR) should be executed after Guest CR is loaded. So the guest CR is restored after the Exit Timing in lightweight_exit executes cmpw, which can clobber CR. Signed-off-by: Bharat Bhushan Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/bookehv_interrupts.S | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/bookehv_interrupts.S b/arch/powerpc/kvm/bookehv_interrupts.S index 57e2fa414444..909e96e0650c 100644 --- a/arch/powerpc/kvm/bookehv_interrupts.S +++ b/arch/powerpc/kvm/bookehv_interrupts.S @@ -580,7 +580,6 @@ lightweight_exit: mtlr r3 mtxer r5 mtctr r6 - mtcr r7 mtsrr0 r8 mtsrr1 r9 @@ -588,14 +587,20 @@ lightweight_exit: /* save enter time */ 1: mfspr r6, SPRN_TBRU - mfspr r7, SPRN_TBRL + mfspr r9, SPRN_TBRL mfspr r8, SPRN_TBRU cmpw r8, r6 - PPC_STL r7, VCPU_TIMING_LAST_ENTER_TBL(r4) + PPC_STL r9, VCPU_TIMING_LAST_ENTER_TBL(r4) bne 1b PPC_STL r8, VCPU_TIMING_LAST_ENTER_TBU(r4) #endif + /* + * Don't execute any instruction which can change CR after + * below instruction. + */ + mtcr r7 + /* Finish loading guest volatiles and jump to guest. */ PPC_LL r5, VCPU_GPR(r5)(r4) PPC_LL r6, VCPU_GPR(r6)(r4) -- cgit v1.2.3 From 7657f4089b097846cc37bfa2b74fc0bd2bd60e30 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Mon, 5 Mar 2012 21:42:25 +0000 Subject: KVM: PPC: Book 3S: Fix compilation for !HV configs Commits 2f5cdd5487 ("KVM: PPC: Book3S HV: Make secondary threads more robust against stray IPIs") and 1c2066b0f7 ("KVM: PPC: Book3S HV: Make virtual processor area registration more robust") added fields to struct kvm_vcpu_arch inside #ifdef CONFIG_KVM_BOOK3S_64_HV regions, and added lines to arch/powerpc/kernel/asm-offsets.c to generate assembler constants for their offsets. Unfortunately this led to compile errors on Book 3S machines for configs that had KVM enabled but not CONFIG_KVM_BOOK3S_64_HV. This fixes the problem by moving the offending lines inside #ifdef CONFIG_KVM_BOOK3S_64_HV regions. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kernel/asm-offsets.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kernel/asm-offsets.c b/arch/powerpc/kernel/asm-offsets.c index 502e038f8c8a..694af3ebb0e4 100644 --- a/arch/powerpc/kernel/asm-offsets.c +++ b/arch/powerpc/kernel/asm-offsets.c @@ -450,6 +450,7 @@ int main(void) DEFINE(KVM_VRMA_SLB_V, offsetof(struct kvm, arch.vrma_slb_v)); DEFINE(VCPU_DSISR, offsetof(struct kvm_vcpu, arch.shregs.dsisr)); DEFINE(VCPU_DAR, offsetof(struct kvm_vcpu, arch.shregs.dar)); + DEFINE(VCPU_VPA, offsetof(struct kvm_vcpu, arch.vpa.pinned_addr)); #endif #ifdef CONFIG_PPC_BOOK3S DEFINE(VCPU_VCPUID, offsetof(struct kvm_vcpu, vcpu_id)); @@ -466,7 +467,6 @@ int main(void) DEFINE(VCPU_PENDING_EXC, offsetof(struct kvm_vcpu, arch.pending_exceptions)); DEFINE(VCPU_CEDED, offsetof(struct kvm_vcpu, arch.ceded)); DEFINE(VCPU_PRODDED, offsetof(struct kvm_vcpu, arch.prodded)); - DEFINE(VCPU_VPA, offsetof(struct kvm_vcpu, arch.vpa.pinned_addr)); DEFINE(VCPU_MMCR, offsetof(struct kvm_vcpu, arch.mmcr)); DEFINE(VCPU_PMC, offsetof(struct kvm_vcpu, arch.pmc)); DEFINE(VCPU_SLB, offsetof(struct kvm_vcpu, arch.slb)); @@ -540,10 +540,10 @@ int main(void) HSTATE_FIELD(HSTATE_IN_GUEST, in_guest); HSTATE_FIELD(HSTATE_RESTORE_HID5, restore_hid5); HSTATE_FIELD(HSTATE_NAPPING, napping); - HSTATE_FIELD(HSTATE_HWTHREAD_REQ, hwthread_req); - HSTATE_FIELD(HSTATE_HWTHREAD_STATE, hwthread_state); #ifdef CONFIG_KVM_BOOK3S_64_HV + HSTATE_FIELD(HSTATE_HWTHREAD_REQ, hwthread_req); + HSTATE_FIELD(HSTATE_HWTHREAD_STATE, hwthread_state); HSTATE_FIELD(HSTATE_KVM_VCPU, kvm_vcpu); HSTATE_FIELD(HSTATE_KVM_VCORE, kvm_vcore); HSTATE_FIELD(HSTATE_XICS_PHYS, xics_phys); -- cgit v1.2.3 From 8943633cf9b87980d261a022e90d94bc2c55df35 Mon Sep 17 00:00:00 2001 From: Paul Mackerras Date: Fri, 2 Mar 2012 01:38:23 +0000 Subject: KVM: PPC: Work around POWER7 DABR corruption problem It turns out that on POWER7, writing to the DABR can cause a corrupted value to be written if the PMU is active and updating SDAR in continuous sampling mode. To work around this, we make sure that the PMU is inactive and SDAR updates are disabled (via MMCRA) when we are context-switching DABR. When the guest sets DABR via the H_SET_DABR hypercall, we use a slightly different workaround, which is to read back the DABR and write it again if it got corrupted. While we are at it, make it consistent that the saving and restoring of the guest's non-volatile GPRs and the FPRs are done with the guest setup of the PMU active. Signed-off-by: Paul Mackerras Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/book3s_hv_interrupts.S | 9 +++- arch/powerpc/kvm/book3s_hv_rmhandlers.S | 93 +++++++++++++++++++-------------- 2 files changed, 61 insertions(+), 41 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/kvm/book3s_hv_interrupts.S b/arch/powerpc/kvm/book3s_hv_interrupts.S index d3fb4df02c41..84035a528c80 100644 --- a/arch/powerpc/kvm/book3s_hv_interrupts.S +++ b/arch/powerpc/kvm/book3s_hv_interrupts.S @@ -68,19 +68,24 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) rotldi r10,r10,16 mtmsrd r10,1 - /* Save host PMU registers and load guest PMU registers */ + /* Save host PMU registers */ /* R4 is live here (vcpu pointer) but not r3 or r5 */ li r3, 1 sldi r3, r3, 31 /* MMCR0_FC (freeze counters) bit */ mfspr r7, SPRN_MMCR0 /* save MMCR0 */ mtspr SPRN_MMCR0, r3 /* freeze all counters, disable interrupts */ + mfspr r6, SPRN_MMCRA +BEGIN_FTR_SECTION + /* On P7, clear MMCRA in order to disable SDAR updates */ + li r5, 0 + mtspr SPRN_MMCRA, r5 +END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) isync ld r3, PACALPPACAPTR(r13) /* is the host using the PMU? */ lbz r5, LPPACA_PMCINUSE(r3) cmpwi r5, 0 beq 31f /* skip if not */ mfspr r5, SPRN_MMCR1 - mfspr r6, SPRN_MMCRA std r7, HSTATE_MMCR(r13) std r5, HSTATE_MMCR + 8(r13) std r6, HSTATE_MMCR + 16(r13) diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S b/arch/powerpc/kvm/book3s_hv_rmhandlers.S index d595033bd449..a84aafce2a12 100644 --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S @@ -159,24 +159,15 @@ kvmppc_hv_entry: mflr r0 std r0, HSTATE_VMHANDLER(r13) - ld r14, VCPU_GPR(r14)(r4) - ld r15, VCPU_GPR(r15)(r4) - ld r16, VCPU_GPR(r16)(r4) - ld r17, VCPU_GPR(r17)(r4) - ld r18, VCPU_GPR(r18)(r4) - ld r19, VCPU_GPR(r19)(r4) - ld r20, VCPU_GPR(r20)(r4) - ld r21, VCPU_GPR(r21)(r4) - ld r22, VCPU_GPR(r22)(r4) - ld r23, VCPU_GPR(r23)(r4) - ld r24, VCPU_GPR(r24)(r4) - ld r25, VCPU_GPR(r25)(r4) - ld r26, VCPU_GPR(r26)(r4) - ld r27, VCPU_GPR(r27)(r4) - ld r28, VCPU_GPR(r28)(r4) - ld r29, VCPU_GPR(r29)(r4) - ld r30, VCPU_GPR(r30)(r4) - ld r31, VCPU_GPR(r31)(r4) + /* Set partition DABR */ + /* Do this before re-enabling PMU to avoid P7 DABR corruption bug */ + li r5,3 + ld r6,VCPU_DABR(r4) + mtspr SPRN_DABRX,r5 + mtspr SPRN_DABR,r6 +BEGIN_FTR_SECTION + isync +END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) /* Load guest PMU registers */ /* R4 is live here (vcpu pointer) */ @@ -215,6 +206,25 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201) /* Load up FP, VMX and VSX registers */ bl kvmppc_load_fp + ld r14, VCPU_GPR(r14)(r4) + ld r15, VCPU_GPR(r15)(r4) + ld r16, VCPU_GPR(r16)(r4) + ld r17, VCPU_GPR(r17)(r4) + ld r18, VCPU_GPR(r18)(r4) + ld r19, VCPU_GPR(r19)(r4) + ld r20, VCPU_GPR(r20)(r4) + ld r21, VCPU_GPR(r21)(r4) + ld r22, VCPU_GPR(r22)(r4) + ld r23, VCPU_GPR(r23)(r4) + ld r24, VCPU_GPR(r24)(r4) + ld r25, VCPU_GPR(r25)(r4) + ld r26, VCPU_GPR(r26)(r4) + ld r27, VCPU_GPR(r27)(r4) + ld r28, VCPU_GPR(r28)(r4) + ld r29, VCPU_GPR(r29)(r4) + ld r30, VCPU_GPR(r30)(r4) + ld r31, VCPU_GPR(r31)(r4) + BEGIN_FTR_SECTION /* Switch DSCR to guest value */ ld r5, VCPU_DSCR(r4) @@ -256,12 +266,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) mtspr SPRN_DAR, r5 mtspr SPRN_DSISR, r6 - /* Set partition DABR */ - li r5,3 - ld r6,VCPU_DABR(r4) - mtspr SPRN_DABRX,r5 - mtspr SPRN_DABR,r6 - BEGIN_FTR_SECTION /* Restore AMR and UAMOR, set AMOR to all 1s */ ld r5,VCPU_AMR(r4) @@ -955,12 +959,6 @@ BEGIN_FTR_SECTION mtspr SPRN_AMR,r6 END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) - /* Restore host DABR and DABRX */ - ld r5,HSTATE_DABR(r13) - li r6,7 - mtspr SPRN_DABR,r5 - mtspr SPRN_DABRX,r6 - /* Switch DSCR back to host value */ BEGIN_FTR_SECTION mfspr r8, SPRN_DSCR @@ -999,6 +997,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) std r5, VCPU_SPRG2(r9) std r6, VCPU_SPRG3(r9) + /* save FP state */ + mr r3, r9 + bl .kvmppc_save_fp + /* Increment yield count if they have a VPA */ ld r8, VCPU_VPA(r9) /* do they have a VPA? */ cmpdi r8, 0 @@ -1013,6 +1015,12 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) sldi r3, r3, 31 /* MMCR0_FC (freeze counters) bit */ mfspr r4, SPRN_MMCR0 /* save MMCR0 */ mtspr SPRN_MMCR0, r3 /* freeze all counters, disable ints */ + mfspr r6, SPRN_MMCRA +BEGIN_FTR_SECTION + /* On P7, clear MMCRA in order to disable SDAR updates */ + li r7, 0 + mtspr SPRN_MMCRA, r7 +END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) isync beq 21f /* if no VPA, save PMU stuff anyway */ lbz r7, LPPACA_PMCINUSE(r8) @@ -1021,7 +1029,6 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) std r3, VCPU_MMCR(r9) /* if not, set saved MMCR0 to FC */ b 22f 21: mfspr r5, SPRN_MMCR1 - mfspr r6, SPRN_MMCRA std r4, VCPU_MMCR(r9) std r5, VCPU_MMCR + 8(r9) std r6, VCPU_MMCR + 16(r9) @@ -1046,17 +1053,20 @@ BEGIN_FTR_SECTION stw r11, VCPU_PMC + 28(r9) END_FTR_SECTION_IFSET(CPU_FTR_ARCH_201) 22: - /* save FP state */ - mr r3, r9 - bl .kvmppc_save_fp /* Secondary threads go off to take a nap on POWER7 */ BEGIN_FTR_SECTION - lwz r0,VCPU_PTID(r3) + lwz r0,VCPU_PTID(r9) cmpwi r0,0 bne secondary_nap END_FTR_SECTION_IFSET(CPU_FTR_ARCH_206) + /* Restore host DABR and DABRX */ + ld r5,HSTATE_DABR(r13) + li r6,7 + mtspr SPRN_DABR,r5 + mtspr SPRN_DABRX,r6 + /* * Reload DEC. HDEC interrupts were disabled when * we reloaded the host's LPCR value. @@ -1393,7 +1403,12 @@ bounce_ext_interrupt: _GLOBAL(kvmppc_h_set_dabr) std r4,VCPU_DABR(r3) - mtspr SPRN_DABR,r4 + /* Work around P7 bug where DABR can get corrupted on mtspr */ +1: mtspr SPRN_DABR,r4 + mfspr r5, SPRN_DABR + cmpd r4, r5 + bne 1b + isync li r3,0 blr @@ -1615,8 +1630,8 @@ kvm_no_guest: * r3 = vcpu pointer */ _GLOBAL(kvmppc_save_fp) - mfmsr r9 - ori r8,r9,MSR_FP + mfmsr r5 + ori r8,r5,MSR_FP #ifdef CONFIG_ALTIVEC BEGIN_FTR_SECTION oris r8,r8,MSR_VEC@h @@ -1665,7 +1680,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC) #endif mfspr r6,SPRN_VRSAVE stw r6,VCPU_VRSAVE(r3) - mtmsrd r9 + mtmsrd r5 isync blr -- cgit v1.2.3 From 6020c0f6e78888b6023559e9bf633ad0092a1709 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Mon, 12 Mar 2012 02:26:30 +0100 Subject: KVM: PPC: Pass EA to updating emulation ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When emulating updating load/store instructions (lwzu, stwu, ...) we need to write the effective address of the load/store into a register. Currently, we write the physical address in there, which is very wrong. So instead let's save off where the virtual fault was on MMIO and use that information as value to put into the register. While at it, also move the XOP variants of the above instructions to the new scheme of using the already known vaddr instead of calculating it themselves. Reported-by: Jörg Sommer Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/include/asm/kvm_host.h | 1 + arch/powerpc/kvm/book3s_64_mmu_hv.c | 5 +++-- arch/powerpc/kvm/book3s_pr.c | 1 + arch/powerpc/kvm/booke.c | 1 + arch/powerpc/kvm/emulate.c | 39 +++++++++++-------------------------- 5 files changed, 17 insertions(+), 30 deletions(-) (limited to 'arch') diff --git a/arch/powerpc/include/asm/kvm_host.h b/arch/powerpc/include/asm/kvm_host.h index 014eaf27a239..42a527e70490 100644 --- a/arch/powerpc/include/asm/kvm_host.h +++ b/arch/powerpc/include/asm/kvm_host.h @@ -464,6 +464,7 @@ struct kvm_vcpu_arch { u32 epr; #endif gpa_t paddr_accessed; + gva_t vaddr_accessed; u8 io_gpr; /* GPR used as IO source/target */ u8 mmio_is_bigendian; diff --git a/arch/powerpc/kvm/book3s_64_mmu_hv.c b/arch/powerpc/kvm/book3s_64_mmu_hv.c index d031ce1d83f5..8e6401f2c16f 100644 --- a/arch/powerpc/kvm/book3s_64_mmu_hv.c +++ b/arch/powerpc/kvm/book3s_64_mmu_hv.c @@ -447,7 +447,7 @@ static int instruction_is_store(unsigned int instr) } static int kvmppc_hv_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu, - unsigned long gpa, int is_store) + unsigned long gpa, gva_t ea, int is_store) { int ret; u32 last_inst; @@ -494,6 +494,7 @@ static int kvmppc_hv_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu, */ vcpu->arch.paddr_accessed = gpa; + vcpu->arch.vaddr_accessed = ea; return kvmppc_emulate_mmio(run, vcpu); } @@ -547,7 +548,7 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, /* No memslot means it's an emulated MMIO region */ if (!memslot || (memslot->flags & KVM_MEMSLOT_INVALID)) { unsigned long gpa = (gfn << PAGE_SHIFT) | (ea & (psize - 1)); - return kvmppc_hv_emulate_mmio(run, vcpu, gpa, + return kvmppc_hv_emulate_mmio(run, vcpu, gpa, ea, dsisr & DSISR_ISSTORE); } diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index 7759053d391b..158047fc9513 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -351,6 +351,7 @@ int kvmppc_handle_pagefault(struct kvm_run *run, struct kvm_vcpu *vcpu, /* MMIO */ vcpu->stat.mmio_exits++; vcpu->arch.paddr_accessed = pte.raddr; + vcpu->arch.vaddr_accessed = pte.eaddr; r = kvmppc_emulate_mmio(run, vcpu); if ( r == RESUME_HOST_NV ) r = RESUME_HOST; diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 9f27258d8035..2675dcb40a7f 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -875,6 +875,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, /* Guest has mapped and accessed a page which is not * actually RAM. */ vcpu->arch.paddr_accessed = gpaddr; + vcpu->arch.vaddr_accessed = eaddr; r = kvmppc_emulate_mmio(run, vcpu); kvmppc_account_exit(vcpu, MMIO_EXITS); } diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c index 968f40101883..e79a620608ab 100644 --- a/arch/powerpc/kvm/emulate.c +++ b/arch/powerpc/kvm/emulate.c @@ -141,7 +141,6 @@ u32 kvmppc_get_dec(struct kvm_vcpu *vcpu, u64 tb) int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) { u32 inst = kvmppc_get_last_inst(vcpu); - u32 ea; int ra; int rb; int rs; @@ -185,12 +184,8 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) ra = get_ra(inst); rb = get_rb(inst); - ea = kvmppc_get_gpr(vcpu, rb); - if (ra) - ea += kvmppc_get_gpr(vcpu, ra); - emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1); - kvmppc_set_gpr(vcpu, ra, ea); + kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed); break; case OP_31_XOP_STWX: @@ -212,14 +207,10 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) ra = get_ra(inst); rb = get_rb(inst); - ea = kvmppc_get_gpr(vcpu, rb); - if (ra) - ea += kvmppc_get_gpr(vcpu, ra); - emulated = kvmppc_handle_store(run, vcpu, kvmppc_get_gpr(vcpu, rs), 1, 1); - kvmppc_set_gpr(vcpu, rs, ea); + kvmppc_set_gpr(vcpu, rs, vcpu->arch.vaddr_accessed); break; case OP_31_XOP_LHAX: @@ -237,12 +228,8 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) ra = get_ra(inst); rb = get_rb(inst); - ea = kvmppc_get_gpr(vcpu, rb); - if (ra) - ea += kvmppc_get_gpr(vcpu, ra); - emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1); - kvmppc_set_gpr(vcpu, ra, ea); + kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed); break; case OP_31_XOP_MFSPR: @@ -318,14 +305,10 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) ra = get_ra(inst); rb = get_rb(inst); - ea = kvmppc_get_gpr(vcpu, rb); - if (ra) - ea += kvmppc_get_gpr(vcpu, ra); - emulated = kvmppc_handle_store(run, vcpu, kvmppc_get_gpr(vcpu, rs), 2, 1); - kvmppc_set_gpr(vcpu, ra, ea); + kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed); break; case OP_31_XOP_MTSPR: @@ -429,7 +412,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) ra = get_ra(inst); rt = get_rt(inst); emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1); - kvmppc_set_gpr(vcpu, ra, vcpu->arch.paddr_accessed); + kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed); break; case OP_LBZ: @@ -441,7 +424,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) ra = get_ra(inst); rt = get_rt(inst); emulated = kvmppc_handle_load(run, vcpu, rt, 1, 1); - kvmppc_set_gpr(vcpu, ra, vcpu->arch.paddr_accessed); + kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed); break; case OP_STW: @@ -457,7 +440,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) emulated = kvmppc_handle_store(run, vcpu, kvmppc_get_gpr(vcpu, rs), 4, 1); - kvmppc_set_gpr(vcpu, ra, vcpu->arch.paddr_accessed); + kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed); break; case OP_STB: @@ -473,7 +456,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) emulated = kvmppc_handle_store(run, vcpu, kvmppc_get_gpr(vcpu, rs), 1, 1); - kvmppc_set_gpr(vcpu, ra, vcpu->arch.paddr_accessed); + kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed); break; case OP_LHZ: @@ -485,7 +468,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) ra = get_ra(inst); rt = get_rt(inst); emulated = kvmppc_handle_load(run, vcpu, rt, 2, 1); - kvmppc_set_gpr(vcpu, ra, vcpu->arch.paddr_accessed); + kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed); break; case OP_LHA: @@ -497,7 +480,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) ra = get_ra(inst); rt = get_rt(inst); emulated = kvmppc_handle_loads(run, vcpu, rt, 2, 1); - kvmppc_set_gpr(vcpu, ra, vcpu->arch.paddr_accessed); + kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed); break; case OP_STH: @@ -513,7 +496,7 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) emulated = kvmppc_handle_store(run, vcpu, kvmppc_get_gpr(vcpu, rs), 2, 1); - kvmppc_set_gpr(vcpu, ra, vcpu->arch.paddr_accessed); + kvmppc_set_gpr(vcpu, ra, vcpu->arch.vaddr_accessed); break; default: -- cgit v1.2.3 From 6df79df5b27d74e0c9803d7f47bb878370996548 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 13 Mar 2012 22:15:45 +0100 Subject: KVM: PPC: Emulate tw and td instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are 4 conditional trapping instructions: tw, twi, td, tdi. The ones with an i take an immediate comparison, the others compare two registers. All of them arrive in the emulator when the condition to trap was successfully fulfilled. Unfortunately, we were only implementing the i versions so far, so let's also add support for the other two. This fixes kernel booting with recents book3s_32 guest kernels. Reported-by: Jörg Sommer Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/emulate.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'arch') diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c index e79a620608ab..afc9154f1aef 100644 --- a/arch/powerpc/kvm/emulate.c +++ b/arch/powerpc/kvm/emulate.c @@ -35,7 +35,9 @@ #define OP_TRAP 3 #define OP_TRAP_64 2 +#define OP_31_XOP_TRAP 4 #define OP_31_XOP_LWZX 23 +#define OP_31_XOP_TRAP_64 68 #define OP_31_XOP_LBZX 87 #define OP_31_XOP_STWX 151 #define OP_31_XOP_STBX 215 @@ -169,6 +171,18 @@ int kvmppc_emulate_instruction(struct kvm_run *run, struct kvm_vcpu *vcpu) case 31: switch (get_xop(inst)) { + case OP_31_XOP_TRAP: +#ifdef CONFIG_64BIT + case OP_31_XOP_TRAP_64: +#endif +#ifdef CONFIG_PPC_BOOK3S + kvmppc_core_queue_program(vcpu, SRR1_PROGTRAP); +#else + kvmppc_core_queue_program(vcpu, + vcpu->arch.shared->esr | ESR_PTR); +#endif + advance = 0; + break; case OP_31_XOP_LWZX: rt = get_rt(inst); emulated = kvmppc_handle_load(run, vcpu, rt, 4, 1); -- cgit v1.2.3 From 4f225ae06e7f39a523ec500c3cf127e50797983e Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Tue, 13 Mar 2012 23:05:16 +0100 Subject: KVM: PPC: Book3s: PR: Add HV traps so we can run in HV=1 mode on p7 When running PR KVM on a p7 system in bare metal, we get HV exits instead of normal supervisor traps. Semantically they are identical though and the HSRR vs SRR difference is already taken care of in the exit code. So all we need to do is handle them in addition to our normal exits. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/book3s_pr.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'arch') diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index 158047fc9513..a7f031b6fc98 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -618,10 +618,13 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, break; /* We're good on these - the host merely wanted to get our attention */ case BOOK3S_INTERRUPT_DECREMENTER: + case BOOK3S_INTERRUPT_HV_DECREMENTER: vcpu->stat.dec_exits++; r = RESUME_GUEST; break; case BOOK3S_INTERRUPT_EXTERNAL: + case BOOK3S_INTERRUPT_EXTERNAL_LEVEL: + case BOOK3S_INTERRUPT_EXTERNAL_HV: vcpu->stat.ext_intr_exits++; r = RESUME_GUEST; break; @@ -629,6 +632,7 @@ int kvmppc_handle_exit(struct kvm_run *run, struct kvm_vcpu *vcpu, r = RESUME_GUEST; break; case BOOK3S_INTERRUPT_PROGRAM: + case BOOK3S_INTERRUPT_H_EMUL_ASSIST: { enum emulation_result er; struct kvmppc_book3s_shadow_vcpu *svcpu; -- cgit v1.2.3 From 966cd0f3bdd422f0b10686fb59d0d456fbbb6398 Mon Sep 17 00:00:00 2001 From: Alexander Graf Date: Wed, 14 Mar 2012 16:55:08 +0100 Subject: KVM: PPC: Ignore unhalt request from kvm_vcpu_block When running kvm_vcpu_block and it realizes that the CPU is actually good to run, we get a request bit set for KVM_REQ_UNHALT. Right now, there's nothing we can do with that bit, so let's unset it right after the call again so we don't get confused in our later checks for pending work. Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/book3s_pr.c | 1 + arch/powerpc/kvm/book3s_pr_papr.c | 1 + arch/powerpc/kvm/booke.c | 1 + 3 files changed, 3 insertions(+) (limited to 'arch') diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index a7f031b6fc98..912e10fbf6ef 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -120,6 +120,7 @@ void kvmppc_set_msr(struct kvm_vcpu *vcpu, u64 msr) if (msr & MSR_POW) { if (!vcpu->arch.pending_exceptions) { kvm_vcpu_block(vcpu); + clear_bit(KVM_REQ_UNHALT, &vcpu->requests); vcpu->stat.halt_wakeup++; /* Unset POW bit after we woke up */ diff --git a/arch/powerpc/kvm/book3s_pr_papr.c b/arch/powerpc/kvm/book3s_pr_papr.c index 6d1bfe246f0a..60ac0e793476 100644 --- a/arch/powerpc/kvm/book3s_pr_papr.c +++ b/arch/powerpc/kvm/book3s_pr_papr.c @@ -224,6 +224,7 @@ int kvmppc_h_pr(struct kvm_vcpu *vcpu, unsigned long cmd) return kvmppc_h_pr_bulk_remove(vcpu); case H_CEDE: kvm_vcpu_block(vcpu); + clear_bit(KVM_REQ_UNHALT, &vcpu->requests); vcpu->stat.halt_wakeup++; return EMULATE_DONE; } diff --git a/arch/powerpc/kvm/booke.c b/arch/powerpc/kvm/booke.c index 2675dcb40a7f..72f13f4a06e0 100644 --- a/arch/powerpc/kvm/booke.c +++ b/arch/powerpc/kvm/booke.c @@ -449,6 +449,7 @@ int kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu) if (vcpu->arch.shared->msr & MSR_WE) { local_irq_enable(); kvm_vcpu_block(vcpu); + clear_bit(KVM_REQ_UNHALT, &vcpu->requests); local_irq_disable(); kvmppc_set_exit_type(vcpu, EMULATED_MTMSRWE_EXITS); -- cgit v1.2.3 From bbcc9c06695243ea23d30de36842df9200c33857 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 13 Mar 2012 21:52:44 +0000 Subject: powerpc/kvm: Fix magic page vs. 32-bit RTAS on ppc64 When the kernel calls into RTAS, it switches to 32-bit mode. The magic page was is longer accessible in that case, causing the patched instructions in the RTAS call wrapper to crash. This fixes it by making available a 32-bit mapping of the magic page in that case. This mapping is flushed whenever we switch the kernel back to 64-bit mode. Signed-off-by: Benjamin Herrenschmidt [agraf: add a check if the magic page is mapped] Signed-off-by: Alexander Graf Signed-off-by: Avi Kivity --- arch/powerpc/kvm/book3s.c | 3 +++ arch/powerpc/kvm/book3s_pr.c | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) (limited to 'arch') diff --git a/arch/powerpc/kvm/book3s.c b/arch/powerpc/kvm/book3s.c index c8ead7b9f03b..3f2a8360c857 100644 --- a/arch/powerpc/kvm/book3s.c +++ b/arch/powerpc/kvm/book3s.c @@ -291,6 +291,9 @@ pfn_t kvmppc_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn) { ulong mp_pa = vcpu->arch.magic_page_pa; + if (!(vcpu->arch.shared->msr & MSR_SF)) + mp_pa = (uint32_t)mp_pa; + /* Magic page override */ if (unlikely(mp_pa) && unlikely(((gfn << PAGE_SHIFT) & KVM_PAM) == diff --git a/arch/powerpc/kvm/book3s_pr.c b/arch/powerpc/kvm/book3s_pr.c index 912e10fbf6ef..dba282e5093f 100644 --- a/arch/powerpc/kvm/book3s_pr.c +++ b/arch/powerpc/kvm/book3s_pr.c @@ -145,6 +145,21 @@ void kvmppc_set_msr(struct kvm_vcpu *vcpu, u64 msr) } } + /* + * When switching from 32 to 64-bit, we may have a stale 32-bit + * magic page around, we need to flush it. Typically 32-bit magic + * page will be instanciated when calling into RTAS. Note: We + * assume that such transition only happens while in kernel mode, + * ie, we never transition from user 32-bit to kernel 64-bit with + * a 32-bit magic page around. + */ + if (vcpu->arch.magic_page_pa && + !(old_msr & MSR_PR) && !(old_msr & MSR_SF) && (msr & MSR_SF)) { + /* going from RTAS to normal kernel code */ + kvmppc_mmu_pte_flush(vcpu, (uint32_t)vcpu->arch.magic_page_pa, + ~0xFFFUL); + } + /* Preload FPU if it's enabled */ if (vcpu->arch.shared->msr & MSR_FP) kvmppc_handle_ext(vcpu, BOOK3S_INTERRUPT_FP_UNAVAIL, MSR_FP); @@ -252,6 +267,9 @@ static int kvmppc_visible_gfn(struct kvm_vcpu *vcpu, gfn_t gfn) { ulong mp_pa = vcpu->arch.magic_page_pa; + if (!(vcpu->arch.shared->msr & MSR_SF)) + mp_pa = (uint32_t)mp_pa; + if (unlikely(mp_pa) && unlikely((mp_pa & KVM_PAM) >> PAGE_SHIFT == gfn)) { return 1; -- cgit v1.2.3 From e9bda3b3d0ce775afe15eaf71922d342cc74991c Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Tue, 20 Mar 2012 23:33:51 -0700 Subject: KVM: VMX: Auto-load on CPUs with VMX Enable x86 feature-based autoloading for the kvm-intel module on CPUs with X86_FEATURE_VMX. Signed-off-by: Josh Triplett Acked-By: Kay Sievers Signed-off-by: Avi Kivity --- arch/x86/kvm/vmx.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'arch') diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index ad85adfef843..52f685635766 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -51,6 +52,12 @@ MODULE_AUTHOR("Qumranet"); MODULE_LICENSE("GPL"); +static const struct x86_cpu_id vmx_cpu_id[] = { + X86_FEATURE_MATCH(X86_FEATURE_VMX), + {} +}; +MODULE_DEVICE_TABLE(x86cpu, vmx_cpu_id); + static bool __read_mostly enable_vpid = 1; module_param_named(vpid, enable_vpid, bool, 0444); -- cgit v1.2.3 From c36fc04ef558c95cff46a8c89d2f804f217335f5 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Thu, 8 Mar 2012 12:45:54 +0100 Subject: KVM: x86: add paging gcc optimization Since most guests will have paging enabled for memory management, add likely() optimization around CR0.PG checks. Signed-off-by: Davidlohr Bueso Signed-off-by: Avi Kivity --- arch/x86/kvm/x86.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index cb80c293cdd8..3d1134ddb885 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -64,7 +64,7 @@ static inline int is_pse(struct kvm_vcpu *vcpu) static inline int is_paging(struct kvm_vcpu *vcpu) { - return kvm_read_cr0_bits(vcpu, X86_CR0_PG); + return likely(kvm_read_cr0_bits(vcpu, X86_CR0_PG)); } static inline u32 bit(int bitno) -- cgit v1.2.3 From 220f773a0013bf6fe2eefd9718ac7471f368fd8e Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Wed, 21 Mar 2012 23:49:39 +0900 Subject: KVM: MMU: Make pte_list_desc fit cache lines well We have PTE_LIST_EXT + 1 pointers in this structure and these 40/20 bytes do not fit cache lines well. Furthermore, some allocators may use 64/32-byte objects for the pte_list_desc cache. This patch solves this problem by changing PTE_LIST_EXT from 4 to 3. For shadow paging, the new size is still large enough to hold both the kernel and process mappings for usual anonymous pages. For file mappings, there may be a slight change in the cache usage. Note: with EPT/NPT we almost always have a single spte in each reverse mapping and we will not see any change by this. Signed-off-by: Takuya Yoshikawa Signed-off-by: Avi Kivity --- arch/x86/kvm/mmu.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index dc5f2459db6c..3213348e3a93 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -135,8 +135,6 @@ module_param(dbg, bool, 0644); #define PT64_PERM_MASK (PT_PRESENT_MASK | PT_WRITABLE_MASK | PT_USER_MASK \ | PT64_NX_MASK) -#define PTE_LIST_EXT 4 - #define ACC_EXEC_MASK 1 #define ACC_WRITE_MASK PT_WRITABLE_MASK #define ACC_USER_MASK PT_USER_MASK @@ -151,6 +149,9 @@ module_param(dbg, bool, 0644); #define SHADOW_PT_INDEX(addr, level) PT64_INDEX(addr, level) +/* make pte_list_desc fit well in cache line */ +#define PTE_LIST_EXT 3 + struct pte_list_desc { u64 *sptes[PTE_LIST_EXT]; struct pte_list_desc *more; -- cgit v1.2.3 From 1e3f42f03c38c29c1814199a6f0a2f01b919ea3f Mon Sep 17 00:00:00 2001 From: Takuya Yoshikawa Date: Wed, 21 Mar 2012 23:50:34 +0900 Subject: KVM: MMU: Improve iteration through sptes from rmap Iteration using rmap_next(), the actual body is pte_list_next(), is inefficient: every time we call it we start from checking whether rmap holds a single spte or points to a descriptor which links more sptes. In the case of shadow paging, this quadratic total iteration cost is a problem. Even for two dimensional paging, with EPT/NPT on, in which we almost always have a single mapping, the extra checks at the end of the iteration should be eliminated. This patch fixes this by introducing rmap_iterator which keeps the iteration context for the next search. Furthermore the implementation of rmap_next() is splitted into two functions, rmap_get_first() and rmap_get_next(), to avoid repeatedly checking whether the rmap being iterated on has only one spte. Although there seemed to be only a slight change for EPT/NPT, the actual improvement was significant: we observed that GET_DIRTY_LOG for 1GB dirty memory became 15% faster than before. This is probably because the new code is easy to make branch predictions. Note: we just remove pte_list_next() because we can think of parent_ptes as a reverse mapping. Signed-off-by: Takuya Yoshikawa Signed-off-by: Avi Kivity --- arch/x86/kvm/mmu.c | 196 ++++++++++++++++++++++++++++------------------- arch/x86/kvm/mmu_audit.c | 10 +-- 2 files changed, 124 insertions(+), 82 deletions(-) (limited to 'arch') diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 3213348e3a93..29ad6f9c58a5 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -842,32 +842,6 @@ static int pte_list_add(struct kvm_vcpu *vcpu, u64 *spte, return count; } -static u64 *pte_list_next(unsigned long *pte_list, u64 *spte) -{ - struct pte_list_desc *desc; - u64 *prev_spte; - int i; - - if (!*pte_list) - return NULL; - else if (!(*pte_list & 1)) { - if (!spte) - return (u64 *)*pte_list; - return NULL; - } - desc = (struct pte_list_desc *)(*pte_list & ~1ul); - prev_spte = NULL; - while (desc) { - for (i = 0; i < PTE_LIST_EXT && desc->sptes[i]; ++i) { - if (prev_spte == spte) - return desc->sptes[i]; - prev_spte = desc->sptes[i]; - } - desc = desc->more; - } - return NULL; -} - static void pte_list_desc_remove_entry(unsigned long *pte_list, struct pte_list_desc *desc, int i, struct pte_list_desc *prev_desc) @@ -988,11 +962,6 @@ static int rmap_add(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn) return pte_list_add(vcpu, spte, rmapp); } -static u64 *rmap_next(unsigned long *rmapp, u64 *spte) -{ - return pte_list_next(rmapp, spte); -} - static void rmap_remove(struct kvm *kvm, u64 *spte) { struct kvm_mmu_page *sp; @@ -1005,6 +974,67 @@ static void rmap_remove(struct kvm *kvm, u64 *spte) pte_list_remove(spte, rmapp); } +/* + * Used by the following functions to iterate through the sptes linked by a + * rmap. All fields are private and not assumed to be used outside. + */ +struct rmap_iterator { + /* private fields */ + struct pte_list_desc *desc; /* holds the sptep if not NULL */ + int pos; /* index of the sptep */ +}; + +/* + * Iteration must be started by this function. This should also be used after + * removing/dropping sptes from the rmap link because in such cases the + * information in the itererator may not be valid. + * + * Returns sptep if found, NULL otherwise. + */ +static u64 *rmap_get_first(unsigned long rmap, struct rmap_iterator *iter) +{ + if (!rmap) + return NULL; + + if (!(rmap & 1)) { + iter->desc = NULL; + return (u64 *)rmap; + } + + iter->desc = (struct pte_list_desc *)(rmap & ~1ul); + iter->pos = 0; + return iter->desc->sptes[iter->pos]; +} + +/* + * Must be used with a valid iterator: e.g. after rmap_get_first(). + * + * Returns sptep if found, NULL otherwise. + */ +static u64 *rmap_get_next(struct rmap_iterator *iter) +{ + if (iter->desc) { + if (iter->pos < PTE_LIST_EXT - 1) { + u64 *sptep; + + ++iter->pos; + sptep = iter->desc->sptes[iter->pos]; + if (sptep) + return sptep; + } + + iter->desc = iter->desc->more; + + if (iter->desc) { + iter->pos = 0; + /* desc->sptes[0] cannot be NULL */ + return iter->desc->sptes[iter->pos]; + } + } + + return NULL; +} + static void drop_spte(struct kvm *kvm, u64 *sptep) { if (mmu_spte_clear_track_bits(sptep)) @@ -1013,23 +1043,27 @@ static void drop_spte(struct kvm *kvm, u64 *sptep) static int __rmap_write_protect(struct kvm *kvm, unsigned long *rmapp, int level) { - u64 *spte = NULL; + u64 *sptep; + struct rmap_iterator iter; int write_protected = 0; - while ((spte = rmap_next(rmapp, spte))) { - BUG_ON(!(*spte & PT_PRESENT_MASK)); - rmap_printk("rmap_write_protect: spte %p %llx\n", spte, *spte); + for (sptep = rmap_get_first(*rmapp, &iter); sptep;) { + BUG_ON(!(*sptep & PT_PRESENT_MASK)); + rmap_printk("rmap_write_protect: spte %p %llx\n", sptep, *sptep); - if (!is_writable_pte(*spte)) + if (!is_writable_pte(*sptep)) { + sptep = rmap_get_next(&iter); continue; + } if (level == PT_PAGE_TABLE_LEVEL) { - mmu_spte_update(spte, *spte & ~PT_WRITABLE_MASK); + mmu_spte_update(sptep, *sptep & ~PT_WRITABLE_MASK); + sptep = rmap_get_next(&iter); } else { - BUG_ON(!is_large_pte(*spte)); - drop_spte(kvm, spte); + BUG_ON(!is_large_pte(*sptep)); + drop_spte(kvm, sptep); --kvm->stat.lpages; - spte = NULL; + sptep = rmap_get_first(*rmapp, &iter); } write_protected = 1; @@ -1084,48 +1118,57 @@ static int rmap_write_protect(struct kvm *kvm, u64 gfn) static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp, unsigned long data) { - u64 *spte; + u64 *sptep; + struct rmap_iterator iter; int need_tlb_flush = 0; - while ((spte = rmap_next(rmapp, NULL))) { - BUG_ON(!(*spte & PT_PRESENT_MASK)); - rmap_printk("kvm_rmap_unmap_hva: spte %p %llx\n", spte, *spte); - drop_spte(kvm, spte); + while ((sptep = rmap_get_first(*rmapp, &iter))) { + BUG_ON(!(*sptep & PT_PRESENT_MASK)); + rmap_printk("kvm_rmap_unmap_hva: spte %p %llx\n", sptep, *sptep); + + drop_spte(kvm, sptep); need_tlb_flush = 1; } + return need_tlb_flush; } static int kvm_set_pte_rmapp(struct kvm *kvm, unsigned long *rmapp, unsigned long data) { + u64 *sptep; + struct rmap_iterator iter; int need_flush = 0; - u64 *spte, new_spte; + u64 new_spte; pte_t *ptep = (pte_t *)data; pfn_t new_pfn; WARN_ON(pte_huge(*ptep)); new_pfn = pte_pfn(*ptep); - spte = rmap_next(rmapp, NULL); - while (spte) { - BUG_ON(!is_shadow_present_pte(*spte)); - rmap_printk("kvm_set_pte_rmapp: spte %p %llx\n", spte, *spte); + + for (sptep = rmap_get_first(*rmapp, &iter); sptep;) { + BUG_ON(!is_shadow_present_pte(*sptep)); + rmap_printk("kvm_set_pte_rmapp: spte %p %llx\n", sptep, *sptep); + need_flush = 1; + if (pte_write(*ptep)) { - drop_spte(kvm, spte); - spte = rmap_next(rmapp, NULL); + drop_spte(kvm, sptep); + sptep = rmap_get_first(*rmapp, &iter); } else { - new_spte = *spte &~ (PT64_BASE_ADDR_MASK); + new_spte = *sptep & ~PT64_BASE_ADDR_MASK; new_spte |= (u64)new_pfn << PAGE_SHIFT; new_spte &= ~PT_WRITABLE_MASK; new_spte &= ~SPTE_HOST_WRITEABLE; new_spte &= ~shadow_accessed_mask; - mmu_spte_clear_track_bits(spte); - mmu_spte_set(spte, new_spte); - spte = rmap_next(rmapp, spte); + + mmu_spte_clear_track_bits(sptep); + mmu_spte_set(sptep, new_spte); + sptep = rmap_get_next(&iter); } } + if (need_flush) kvm_flush_remote_tlbs(kvm); @@ -1184,7 +1227,8 @@ void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte) static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp, unsigned long data) { - u64 *spte; + u64 *sptep; + struct rmap_iterator iter; int young = 0; /* @@ -1197,25 +1241,24 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp, if (!shadow_accessed_mask) return kvm_unmap_rmapp(kvm, rmapp, data); - spte = rmap_next(rmapp, NULL); - while (spte) { - int _young; - u64 _spte = *spte; - BUG_ON(!(_spte & PT_PRESENT_MASK)); - _young = _spte & PT_ACCESSED_MASK; - if (_young) { + for (sptep = rmap_get_first(*rmapp, &iter); sptep; + sptep = rmap_get_next(&iter)) { + BUG_ON(!(*sptep & PT_PRESENT_MASK)); + + if (*sptep & PT_ACCESSED_MASK) { young = 1; - clear_bit(PT_ACCESSED_SHIFT, (unsigned long *)spte); + clear_bit(PT_ACCESSED_SHIFT, (unsigned long *)sptep); } - spte = rmap_next(rmapp, spte); } + return young; } static int kvm_test_age_rmapp(struct kvm *kvm, unsigned long *rmapp, unsigned long data) { - u64 *spte; + u64 *sptep; + struct rmap_iterator iter; int young = 0; /* @@ -1226,16 +1269,14 @@ static int kvm_test_age_rmapp(struct kvm *kvm, unsigned long *rmapp, if (!shadow_accessed_mask) goto out; - spte = rmap_next(rmapp, NULL); - while (spte) { - u64 _spte = *spte; - BUG_ON(!(_spte & PT_PRESENT_MASK)); - young = _spte & PT_ACCESSED_MASK; - if (young) { + for (sptep = rmap_get_first(*rmapp, &iter); sptep; + sptep = rmap_get_next(&iter)) { + BUG_ON(!(*sptep & PT_PRESENT_MASK)); + + if (*sptep & PT_ACCESSED_MASK) { young = 1; break; } - spte = rmap_next(rmapp, spte); } out: return young; @@ -1887,10 +1928,11 @@ static void kvm_mmu_put_page(struct kvm_mmu_page *sp, u64 *parent_pte) static void kvm_mmu_unlink_parents(struct kvm *kvm, struct kvm_mmu_page *sp) { - u64 *parent_pte; + u64 *sptep; + struct rmap_iterator iter; - while ((parent_pte = pte_list_next(&sp->parent_ptes, NULL))) - drop_parent_pte(sp, parent_pte); + while ((sptep = rmap_get_first(sp->parent_ptes, &iter))) + drop_parent_pte(sp, sptep); } static int mmu_zap_unsync_children(struct kvm *kvm, diff --git a/arch/x86/kvm/mmu_audit.c b/arch/x86/kvm/mmu_audit.c index 715da5a19a5b..7d7d0b9e23eb 100644 --- a/arch/x86/kvm/mmu_audit.c +++ b/arch/x86/kvm/mmu_audit.c @@ -192,7 +192,8 @@ static void audit_write_protection(struct kvm *kvm, struct kvm_mmu_page *sp) { struct kvm_memory_slot *slot; unsigned long *rmapp; - u64 *spte; + u64 *sptep; + struct rmap_iterator iter; if (sp->role.direct || sp->unsync || sp->role.invalid) return; @@ -200,13 +201,12 @@ static void audit_write_protection(struct kvm *kvm, struct kvm_mmu_page *sp) slot = gfn_to_memslot(kvm, sp->gfn); rmapp = &slot->rmap[sp->gfn - slot->base_gfn]; - spte = rmap_next(rmapp, NULL); - while (spte) { - if (is_writable_pte(*spte)) + for (sptep = rmap_get_first(*rmapp, &iter); sptep; + sptep = rmap_get_next(&iter)) { + if (is_writable_pte(*sptep)) audit_printk(kvm, "shadow page has writable " "mappings: gfn %llx role %x\n", sp->gfn, sp->role.word); - spte = rmap_next(rmapp, spte); } } -- cgit v1.2.3 From a4196febbe75e2cc8fcb7af6460d2c3ef208d66c Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 24 Mar 2012 21:58:45 +0530 Subject: video: s3c-fb: move video interface timing out of window setup data The video interface timing is independent of the window setup data. The resolution of the window can be smaller than that of the lcd panel to which the video data is output. So move the video timing data from the per-window setup data to the platform specific section in the platform data. This also removes the restriction that atleast one window should have the same resolution as that of the panel attached. Cc: Ben Dooks Signed-off-by: Thomas Abraham Acked-by: Jingoo Han Signed-off-by: Florian Tobias Schandinat --- arch/arm/plat-samsung/include/plat/fb.h | 9 ++- drivers/video/s3c-fb.c | 112 +++++++++++++++++--------------- 2 files changed, 67 insertions(+), 54 deletions(-) (limited to 'arch') diff --git a/arch/arm/plat-samsung/include/plat/fb.h b/arch/arm/plat-samsung/include/plat/fb.h index 0fedf47fa502..39d6bd7af308 100644 --- a/arch/arm/plat-samsung/include/plat/fb.h +++ b/arch/arm/plat-samsung/include/plat/fb.h @@ -24,15 +24,16 @@ /** * struct s3c_fb_pd_win - per window setup data - * @win_mode: The display parameters to initialise (not for window 0) + * @xres : The window X size. + * @yres : The window Y size. * @virtual_x: The virtual X size. * @virtual_y: The virtual Y size. */ struct s3c_fb_pd_win { - struct fb_videomode win_mode; - unsigned short default_bpp; unsigned short max_bpp; + unsigned short xres; + unsigned short yres; unsigned short virtual_x; unsigned short virtual_y; }; @@ -45,6 +46,7 @@ struct s3c_fb_pd_win { * @default_win: default window layer number to be used for UI layer. * @vidcon0: The base vidcon0 values to control the panel data format. * @vidcon1: The base vidcon1 values to control the panel data output. + * @vtiming: Video timing when connected to a RGB type panel. * @win: The setup data for each hardware window, or NULL for unused. * @display_mode: The LCD output display mode. * @@ -58,6 +60,7 @@ struct s3c_fb_platdata { void (*setup_gpio)(void); struct s3c_fb_pd_win *win[S3C_FB_MAX_WIN]; + struct fb_videomode *vtiming; u32 default_win; diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index f3105160bf98..c94f40d2cc2e 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -495,7 +495,6 @@ static int s3c_fb_set_par(struct fb_info *info) u32 alpha = 0; u32 data; u32 pagewidth; - int clkdiv; dev_dbg(sfb->dev, "setting framebuffer parameters\n"); @@ -532,48 +531,9 @@ static int s3c_fb_set_par(struct fb_info *info) /* disable the window whilst we update it */ writel(0, regs + WINCON(win_no)); - /* use platform specified window as the basis for the lcd timings */ - - if (win_no == sfb->pdata->default_win) { - clkdiv = s3c_fb_calc_pixclk(sfb, var->pixclock); - - data = sfb->pdata->vidcon0; - data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); - - if (clkdiv > 1) - data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR; - else - data &= ~VIDCON0_CLKDIR; /* 1:1 clock */ - - /* write the timing data to the panel */ - - if (sfb->variant.is_2443) - data |= (1 << 5); - - writel(data, regs + VIDCON0); - + if (win_no == sfb->pdata->default_win) s3c_fb_enable(sfb, 1); - data = VIDTCON0_VBPD(var->upper_margin - 1) | - VIDTCON0_VFPD(var->lower_margin - 1) | - VIDTCON0_VSPW(var->vsync_len - 1); - - writel(data, regs + sfb->variant.vidtcon); - - data = VIDTCON1_HBPD(var->left_margin - 1) | - VIDTCON1_HFPD(var->right_margin - 1) | - VIDTCON1_HSPW(var->hsync_len - 1); - - /* VIDTCON1 */ - writel(data, regs + sfb->variant.vidtcon + 4); - - data = VIDTCON2_LINEVAL(var->yres - 1) | - VIDTCON2_HOZVAL(var->xres - 1) | - VIDTCON2_LINEVAL_E(var->yres - 1) | - VIDTCON2_HOZVAL_E(var->xres - 1); - writel(data, regs + sfb->variant.vidtcon + 8); - } - /* write the buffer address */ /* start and end registers stride is 8 */ @@ -1144,11 +1104,11 @@ static int __devinit s3c_fb_alloc_memory(struct s3c_fb *sfb, dev_dbg(sfb->dev, "allocating memory for display\n"); - real_size = windata->win_mode.xres * windata->win_mode.yres; + real_size = windata->xres * windata->yres; virt_size = windata->virtual_x * windata->virtual_y; dev_dbg(sfb->dev, "real_size=%u (%u.%u), virt_size=%u (%u.%u)\n", - real_size, windata->win_mode.xres, windata->win_mode.yres, + real_size, windata->xres, windata->yres, virt_size, windata->virtual_x, windata->virtual_y); size = (real_size > virt_size) ? real_size : virt_size; @@ -1230,7 +1190,7 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, struct s3c_fb_win **res) { struct fb_var_screeninfo *var; - struct fb_videomode *initmode; + struct fb_videomode initmode; struct s3c_fb_pd_win *windata; struct s3c_fb_win *win; struct fb_info *fbinfo; @@ -1251,11 +1211,11 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, } windata = sfb->pdata->win[win_no]; - initmode = &windata->win_mode; + initmode = *sfb->pdata->vtiming; WARN_ON(windata->max_bpp == 0); - WARN_ON(windata->win_mode.xres == 0); - WARN_ON(windata->win_mode.yres == 0); + WARN_ON(windata->xres == 0); + WARN_ON(windata->yres == 0); win = fbinfo->par; *res = win; @@ -1294,7 +1254,9 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, } /* setup the initial video mode from the window */ - fb_videomode_to_var(&fbinfo->var, initmode); + initmode.xres = windata->xres; + initmode.yres = windata->yres; + fb_videomode_to_var(&fbinfo->var, &initmode); fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; fbinfo->fix.accel = FB_ACCEL_NONE; @@ -1338,6 +1300,53 @@ static int __devinit s3c_fb_probe_win(struct s3c_fb *sfb, unsigned int win_no, return 0; } +/** + * s3c_fb_set_rgb_timing() - set video timing for rgb interface. + * @sfb: The base resources for the hardware. + * + * Set horizontal and vertical lcd rgb interface timing. + */ +static void s3c_fb_set_rgb_timing(struct s3c_fb *sfb) +{ + struct fb_videomode *vmode = sfb->pdata->vtiming; + void __iomem *regs = sfb->regs; + int clkdiv; + u32 data; + + if (!vmode->pixclock) + s3c_fb_missing_pixclock(vmode); + + clkdiv = s3c_fb_calc_pixclk(sfb, vmode->pixclock); + + data = sfb->pdata->vidcon0; + data &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR); + + if (clkdiv > 1) + data |= VIDCON0_CLKVAL_F(clkdiv-1) | VIDCON0_CLKDIR; + else + data &= ~VIDCON0_CLKDIR; /* 1:1 clock */ + + if (sfb->variant.is_2443) + data |= (1 << 5); + writel(data, regs + VIDCON0); + + data = VIDTCON0_VBPD(vmode->upper_margin - 1) | + VIDTCON0_VFPD(vmode->lower_margin - 1) | + VIDTCON0_VSPW(vmode->vsync_len - 1); + writel(data, regs + sfb->variant.vidtcon); + + data = VIDTCON1_HBPD(vmode->left_margin - 1) | + VIDTCON1_HFPD(vmode->right_margin - 1) | + VIDTCON1_HSPW(vmode->hsync_len - 1); + writel(data, regs + sfb->variant.vidtcon + 4); + + data = VIDTCON2_LINEVAL(vmode->yres - 1) | + VIDTCON2_HOZVAL(vmode->xres - 1) | + VIDTCON2_LINEVAL_E(vmode->yres - 1) | + VIDTCON2_HOZVAL_E(vmode->xres - 1); + writel(data, regs + sfb->variant.vidtcon + 8); +} + /** * s3c_fb_clear_win() - clear hardware window registers. * @sfb: The base resources for the hardware. @@ -1481,15 +1490,14 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) writel(0xffffff, regs + WKEYCON1); } + s3c_fb_set_rgb_timing(sfb); + /* we have the register setup, start allocating framebuffers */ for (win = 0; win < fbdrv->variant.nr_windows; win++) { if (!pd->win[win]) continue; - if (!pd->win[win]->win_mode.pixclock) - s3c_fb_missing_pixclock(&pd->win[win]->win_mode); - ret = s3c_fb_probe_win(sfb, win, fbdrv->win[win], &sfb->windows[win]); if (ret < 0) { @@ -1623,6 +1631,8 @@ static int s3c_fb_resume(struct device *dev) shadow_protect_win(win, 0); } + s3c_fb_set_rgb_timing(sfb); + /* restore framebuffers */ for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) { win = sfb->windows[win_no]; -- cgit v1.2.3 From 3c582647a9f84affd5c86e89d548157c62f8d9ca Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 24 Mar 2012 21:58:46 +0530 Subject: video: s3c-fb: remove 'default_win' element from platform data The decision to enable or disable the data output to the lcd panel from the controller need not be based on the value of 'default_win' element in the platform data. Instead, the data output to the panel is enabled if any of the windows are active, else data output is disabled. Cc: Ben Dooks Signed-off-by: Jingoo Han Signed-off-by: Thomas Abraham Signed-off-by: Florian Tobias Schandinat --- arch/arm/plat-samsung/include/plat/fb.h | 2 -- drivers/video/s3c-fb.c | 25 +++++-------------------- 2 files changed, 5 insertions(+), 22 deletions(-) (limited to 'arch') diff --git a/arch/arm/plat-samsung/include/plat/fb.h b/arch/arm/plat-samsung/include/plat/fb.h index 39d6bd7af308..536002ff2ab8 100644 --- a/arch/arm/plat-samsung/include/plat/fb.h +++ b/arch/arm/plat-samsung/include/plat/fb.h @@ -62,8 +62,6 @@ struct s3c_fb_platdata { struct s3c_fb_pd_win *win[S3C_FB_MAX_WIN]; struct fb_videomode *vtiming; - u32 default_win; - u32 vidcon0; u32 vidcon1; }; diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index c94f40d2cc2e..18c84b8d45b5 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c @@ -531,7 +531,7 @@ static int s3c_fb_set_par(struct fb_info *info) /* disable the window whilst we update it */ writel(0, regs + WINCON(win_no)); - if (win_no == sfb->pdata->default_win) + if (!sfb->output_on) s3c_fb_enable(sfb, 1); /* write the buffer address */ @@ -799,6 +799,7 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) struct s3c_fb *sfb = win->parent; unsigned int index = win->index; u32 wincon; + u32 output_on = sfb->output_on; dev_dbg(sfb->dev, "blank mode %d\n", blank_mode); @@ -837,34 +838,18 @@ static int s3c_fb_blank(int blank_mode, struct fb_info *info) shadow_protect_win(win, 1); writel(wincon, sfb->regs + sfb->variant.wincon + (index * 4)); - shadow_protect_win(win, 0); /* Check the enabled state to see if we need to be running the * main LCD interface, as if there are no active windows then * it is highly likely that we also do not need to output * anything. */ - - /* We could do something like the following code, but the current - * system of using framebuffer events means that we cannot make - * the distinction between just window 0 being inactive and all - * the windows being down. - * - * s3c_fb_enable(sfb, sfb->enabled ? 1 : 0); - */ - - /* we're stuck with this until we can do something about overriding - * the power control using the blanking event for a single fb. - */ - if (index == sfb->pdata->default_win) { - shadow_protect_win(win, 1); - s3c_fb_enable(sfb, blank_mode != FB_BLANK_POWERDOWN ? 1 : 0); - shadow_protect_win(win, 0); - } + s3c_fb_enable(sfb, sfb->enabled ? 1 : 0); + shadow_protect_win(win, 0); pm_runtime_put_sync(sfb->dev); - return 0; + return output_on == sfb->output_on; } /** -- cgit v1.2.3 From 884924be2a3368a3ac96587f347f9973c80140f6 Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 24 Mar 2012 21:58:47 +0530 Subject: ARM: s3c64xx: Decouple lcd type from display controller window data The display controller window data should contain window configuration data for only one type of lcd panel. So, for real6410 and mini6410 boards, split the existing display controller window data, which contains window configuration data for two different types of lcd panels, into two seperate instances and register one of them depending on the type of the lcd panel detected at runtime. This is a prerequisite change for a subsequent commit that reorders the platform data of display controller by moving video interface timing out of window setup data. Cc: Darius Augulis Acked-by: Jingoo Han Signed-off-by: Thomas Abraham Signed-off-by: Florian Tobias Schandinat --- arch/arm/mach-s3c64xx/mach-mini6410.c | 82 +++++++++++++++++++---------------- arch/arm/mach-s3c64xx/mach-real6410.c | 82 +++++++++++++++++++---------------- 2 files changed, 88 insertions(+), 76 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-s3c64xx/mach-mini6410.c b/arch/arm/mach-s3c64xx/mach-mini6410.c index c34c2ab22ead..34f51950b46e 100644 --- a/arch/arm/mach-s3c64xx/mach-mini6410.c +++ b/arch/arm/mach-s3c64xx/mach-mini6410.c @@ -151,41 +151,49 @@ static struct s3c2410_platform_nand mini6410_nand_info = { .sets = mini6410_nand_sets, }; -static struct s3c_fb_pd_win mini6410_fb_win[] = { - { - .win_mode = { /* 4.3" 480x272 */ - .left_margin = 3, - .right_margin = 2, - .upper_margin = 1, - .lower_margin = 1, - .hsync_len = 40, - .vsync_len = 1, - .xres = 480, - .yres = 272, - }, - .max_bpp = 32, - .default_bpp = 16, - }, { - .win_mode = { /* 7.0" 800x480 */ - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, - .max_bpp = 32, - .default_bpp = 16, +static struct s3c_fb_pd_win mini6410_lcd_type0_fb_win = { + .win_mode = { /* 4.3" 480x272 */ + .left_margin = 3, + .right_margin = 2, + .upper_margin = 1, + .lower_margin = 1, + .hsync_len = 40, + .vsync_len = 1, + .xres = 480, + .yres = 272, }, + .max_bpp = 32, + .default_bpp = 16, }; -static struct s3c_fb_platdata mini6410_lcd_pdata __initdata = { - .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, - .win[0] = &mini6410_fb_win[0], - .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, - .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, +static struct s3c_fb_pd_win mini6410_lcd_type1_fb_win = { + .win_mode = { /* 7.0" 800x480 */ + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, + }, + .max_bpp = 32, + .default_bpp = 16, +}; + +static struct s3c_fb_platdata mini6410_lcd_pdata[] __initdata = { + { + .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .win[0] = &mini6410_lcd_type0_fb_win, + .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, + .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, + }, { + .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .win[0] = &mini6410_lcd_type1_fb_win, + .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, + .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, + }, + { }, }; static void mini6410_lcd_power_set(struct plat_lcd_data *pd, @@ -283,7 +291,7 @@ static void mini6410_parse_features( "screen type already set\n", f); } else { int li = f - '0'; - if (li >= ARRAY_SIZE(mini6410_fb_win)) + if (li >= ARRAY_SIZE(mini6410_lcd_pdata)) printk(KERN_INFO "MINI6410: '%c' out " "of range LCD mode\n", f); else { @@ -307,14 +315,12 @@ static void __init mini6410_machine_init(void) /* Parse the feature string */ mini6410_parse_features(&features, mini6410_features_str); - mini6410_lcd_pdata.win[0] = &mini6410_fb_win[features.lcd_index]; - printk(KERN_INFO "MINI6410: selected LCD display is %dx%d\n", - mini6410_lcd_pdata.win[0]->win_mode.xres, - mini6410_lcd_pdata.win[0]->win_mode.yres); + mini6410_lcd_pdata[features.lcd_index].win[0]->win_mode.xres, + mini6410_lcd_pdata[features.lcd_index].win[0]->win_mode.yres); s3c_nand_set_platdata(&mini6410_nand_info); - s3c_fb_set_platdata(&mini6410_lcd_pdata); + s3c_fb_set_platdata(&mini6410_lcd_pdata[features.lcd_index]); s3c24xx_ts_set_platdata(NULL); /* configure nCS1 width to 16 bits */ diff --git a/arch/arm/mach-s3c64xx/mach-real6410.c b/arch/arm/mach-s3c64xx/mach-real6410.c index be2a9a22ab74..21f91e5b093d 100644 --- a/arch/arm/mach-s3c64xx/mach-real6410.c +++ b/arch/arm/mach-s3c64xx/mach-real6410.c @@ -117,41 +117,49 @@ static struct platform_device real6410_device_eth = { }, }; -static struct s3c_fb_pd_win real6410_fb_win[] = { - { - .win_mode = { /* 4.3" 480x272 */ - .left_margin = 3, - .right_margin = 2, - .upper_margin = 1, - .lower_margin = 1, - .hsync_len = 40, - .vsync_len = 1, - .xres = 480, - .yres = 272, - }, - .max_bpp = 32, - .default_bpp = 16, - }, { - .win_mode = { /* 7.0" 800x480 */ - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, - .max_bpp = 32, - .default_bpp = 16, +static struct s3c_fb_pd_win real6410_lcd_type0_fb_win = { + .win_mode = { /* 4.3" 480x272 */ + .left_margin = 3, + .right_margin = 2, + .upper_margin = 1, + .lower_margin = 1, + .hsync_len = 40, + .vsync_len = 1, + .xres = 480, + .yres = 272, }, + .max_bpp = 32, + .default_bpp = 16, }; -static struct s3c_fb_platdata real6410_lcd_pdata __initdata = { - .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, - .win[0] = &real6410_fb_win[0], - .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, - .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, +static struct s3c_fb_pd_win real6410_lcd_type1_fb_win = { + .win_mode = { /* 7.0" 800x480 */ + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, + }, + .max_bpp = 32, + .default_bpp = 16, +}; + +static struct s3c_fb_platdata real6410_lcd_pdata[] __initdata = { + { + .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .win[0] = &real6410_lcd_type0_fb_win, + .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, + .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, + }, { + .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .win[0] = &real6410_lcd_type1_fb_win, + .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, + .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, + }, + { }, }; static struct mtd_partition real6410_nand_part[] = { @@ -264,7 +272,7 @@ static void real6410_parse_features( "screen type already set\n", f); } else { int li = f - '0'; - if (li >= ARRAY_SIZE(real6410_fb_win)) + if (li >= ARRAY_SIZE(real6410_lcd_pdata)) printk(KERN_INFO "REAL6410: '%c' out " "of range LCD mode\n", f); else { @@ -288,13 +296,11 @@ static void __init real6410_machine_init(void) /* Parse the feature string */ real6410_parse_features(&features, real6410_features_str); - real6410_lcd_pdata.win[0] = &real6410_fb_win[features.lcd_index]; - printk(KERN_INFO "REAL6410: selected LCD display is %dx%d\n", - real6410_lcd_pdata.win[0]->win_mode.xres, - real6410_lcd_pdata.win[0]->win_mode.yres); + real6410_lcd_pdata[features.lcd_index].win[0]->win_mode.xres, + real6410_lcd_pdata[features.lcd_index].win[0]->win_mode.yres); - s3c_fb_set_platdata(&real6410_lcd_pdata); + s3c_fb_set_platdata(&real6410_lcd_pdata[features.lcd_index]); s3c_nand_set_platdata(&real6410_nand_info); s3c24xx_ts_set_platdata(NULL); -- cgit v1.2.3 From 79d3c41ae444fb064cc8aab7cb249f577d6e0aae Mon Sep 17 00:00:00 2001 From: Thomas Abraham Date: Sat, 24 Mar 2012 21:58:48 +0530 Subject: ARM: Samsung: Rework platform data of s3c-fb driver For all the Samsung SoC based boards which have the platform data for s3c-fb driver, the 'default_win' element in the platform data is removed and the lcd panel video timing values are moved out of individual window configuration data. Acked-by: Jingoo Han Cc: Kyungmin Park Cc: JeongHyeon Kim Cc: Kukjin Kim Cc: Heiko Stuebner Cc: Ben Dooks Cc: Kwangwoo Lee Cc: Mark Brown Cc: Peter Korsgaard Cc: Darius Augulis Cc: Maurus Cuelenaere Signed-off-by: Thomas Abraham Signed-off-by: Florian Tobias Schandinat --- arch/arm/mach-exynos/mach-nuri.c | 26 ++++++++------ arch/arm/mach-exynos/mach-origen.c | 24 +++++++------ arch/arm/mach-exynos/mach-smdkv310.c | 28 +++++++++------- arch/arm/mach-exynos/mach-universal_c210.c | 26 ++++++++------ arch/arm/mach-s3c24xx/mach-smdk2416.c | 27 ++++++++------- arch/arm/mach-s3c64xx/mach-anw6410.c | 25 ++++++++------ arch/arm/mach-s3c64xx/mach-crag6410.c | 25 ++++++++------ arch/arm/mach-s3c64xx/mach-hmt.c | 24 +++++++------ arch/arm/mach-s3c64xx/mach-mini6410.c | 54 ++++++++++++++++++------------ arch/arm/mach-s3c64xx/mach-real6410.c | 52 ++++++++++++++++------------ arch/arm/mach-s3c64xx/mach-smartq5.c | 26 ++++++++------ arch/arm/mach-s3c64xx/mach-smartq7.c | 26 ++++++++------ arch/arm/mach-s3c64xx/mach-smdk6410.c | 25 ++++++++------ arch/arm/mach-s5p64x0/mach-smdk6440.c | 24 +++++++------ arch/arm/mach-s5p64x0/mach-smdk6450.c | 24 +++++++------ arch/arm/mach-s5pc100/mach-smdkc100.c | 27 ++++++++------- arch/arm/mach-s5pv210/mach-aquila.c | 36 +++++++++----------- arch/arm/mach-s5pv210/mach-goni.c | 26 ++++++++------ arch/arm/mach-s5pv210/mach-smdkv210.c | 24 +++++++------ 19 files changed, 311 insertions(+), 238 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index b3982c867c9c..e1e640e5b52a 100644 --- a/arch/arm/mach-exynos/mach-nuri.c +++ b/arch/arm/mach-exynos/mach-nuri.c @@ -214,25 +214,29 @@ static struct platform_device nuri_gpio_keys = { /* Frame Buffer */ static struct s3c_fb_pd_win nuri_fb_win0 = { - .win_mode = { - .left_margin = 64, - .right_margin = 16, - .upper_margin = 64, - .lower_margin = 1, - .hsync_len = 48, - .vsync_len = 3, - .xres = 1024, - .yres = 600, - .refresh = 60, - }, .max_bpp = 24, .default_bpp = 16, + .xres = 1024, + .yres = 600, .virtual_x = 1024, .virtual_y = 2 * 600, }; +static struct fb_videomode nuri_lcd_timing = { + .left_margin = 64, + .right_margin = 16, + .upper_margin = 64, + .lower_margin = 1, + .hsync_len = 48, + .vsync_len = 3, + .xres = 1024, + .yres = 600, + .refresh = 60, +}; + static struct s3c_fb_platdata nuri_fb_pdata __initdata = { .win[0] = &nuri_fb_win0, + .vtiming = &nuri_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB | VIDCON0_CLKSEL_LCD, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c index 878d4c99142d..0968ebaa01f5 100644 --- a/arch/arm/mach-exynos/mach-origen.c +++ b/arch/arm/mach-exynos/mach-origen.c @@ -584,22 +584,26 @@ static struct platform_device origen_lcd_hv070wsa = { }; static struct s3c_fb_pd_win origen_fb_win0 = { - .win_mode = { - .left_margin = 64, - .right_margin = 16, - .upper_margin = 64, - .lower_margin = 16, - .hsync_len = 48, - .vsync_len = 3, - .xres = 1024, - .yres = 600, - }, + .xres = 1024, + .yres = 600, .max_bpp = 32, .default_bpp = 24, }; +static struct fb_videomode origen_lcd_timing = { + .left_margin = 64, + .right_margin = 16, + .upper_margin = 64, + .lower_margin = 16, + .hsync_len = 48, + .vsync_len = 3, + .xres = 1024, + .yres = 600, +}; + static struct s3c_fb_platdata origen_lcd_pdata __initdata = { .win[0] = &origen_fb_win0, + .vtiming = &origen_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC | VIDCON1_INV_VCLK, diff --git a/arch/arm/mach-exynos/mach-smdkv310.c b/arch/arm/mach-exynos/mach-smdkv310.c index 83b91fa777c1..3e482b8c3af8 100644 --- a/arch/arm/mach-exynos/mach-smdkv310.c +++ b/arch/arm/mach-exynos/mach-smdkv310.c @@ -161,22 +161,26 @@ static struct platform_device smdkv310_lcd_lte480wv = { }; static struct s3c_fb_pd_win smdkv310_fb_win0 = { - .win_mode = { - .left_margin = 13, - .right_margin = 8, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, - .max_bpp = 32, - .default_bpp = 24, + .max_bpp = 32, + .default_bpp = 24, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode smdkv310_lcd_timing = { + .left_margin = 13, + .right_margin = 8, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, }; static struct s3c_fb_platdata smdkv310_lcd0_pdata __initdata = { .win[0] = &smdkv310_fb_win0, + .vtiming = &smdkv310_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, .setup_gpio = exynos4_fimd0_gpio_setup_24bpp, diff --git a/arch/arm/mach-exynos/mach-universal_c210.c b/arch/arm/mach-exynos/mach-universal_c210.c index 6bb9dbdd73fd..879c539923cf 100644 --- a/arch/arm/mach-exynos/mach-universal_c210.c +++ b/arch/arm/mach-exynos/mach-universal_c210.c @@ -812,25 +812,29 @@ static struct i2c_board_info i2c1_devs[] __initdata = { /* Frame Buffer */ static struct s3c_fb_pd_win universal_fb_win0 = { - .win_mode = { - .left_margin = 16, - .right_margin = 16, - .upper_margin = 2, - .lower_margin = 28, - .hsync_len = 2, - .vsync_len = 1, - .xres = 480, - .yres = 800, - .refresh = 55, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 480, + .yres = 800, .virtual_x = 480, .virtual_y = 2 * 800, }; +static struct fb_videomode universal_lcd_timing = { + .left_margin = 16, + .right_margin = 16, + .upper_margin = 2, + .lower_margin = 28, + .hsync_len = 2, + .vsync_len = 1, + .xres = 480, + .yres = 800, + .refresh = 55, +}; + static struct s3c_fb_platdata universal_lcd_pdata __initdata = { .win[0] = &universal_fb_win0, + .vtiming = &universal_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB | VIDCON0_CLKSEL_LCD, .vidcon1 = VIDCON1_INV_VCLK | VIDCON1_INV_VDEN diff --git a/arch/arm/mach-s3c24xx/mach-smdk2416.c b/arch/arm/mach-s3c24xx/mach-smdk2416.c index 30a44f806e01..c3100a044fbe 100644 --- a/arch/arm/mach-s3c24xx/mach-smdk2416.c +++ b/arch/arm/mach-s3c24xx/mach-smdk2416.c @@ -148,23 +148,25 @@ static struct s3c24xx_hsudc_platdata smdk2416_hsudc_platdata = { static struct s3c_fb_pd_win smdk2416_fb_win[] = { [0] = { - /* think this is the same as the smdk6410 */ - .win_mode = { - .pixclock = 41094, - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .default_bpp = 16, .max_bpp = 32, + .xres = 800, + .yres = 480, }, }; +static struct fb_videomode smdk2416_lcd_timing = { + .pixclock = 41094, + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, +}; + static void s3c2416_fb_gpio_setup_24bpp(void) { unsigned int gpio; @@ -187,6 +189,7 @@ static void s3c2416_fb_gpio_setup_24bpp(void) static struct s3c_fb_platdata smdk2416_fb_platdata = { .win[0] = &smdk2416_fb_win[0], + .vtiming = &smdk2416_lcd_timing, .setup_gpio = s3c2416_fb_gpio_setup_24bpp, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, diff --git a/arch/arm/mach-s3c64xx/mach-anw6410.c b/arch/arm/mach-s3c64xx/mach-anw6410.c index b86f2779e4e6..58fd0e3ad2b4 100644 --- a/arch/arm/mach-s3c64xx/mach-anw6410.c +++ b/arch/arm/mach-s3c64xx/mach-anw6410.c @@ -134,24 +134,27 @@ static struct platform_device anw6410_lcd_powerdev = { }; static struct s3c_fb_pd_win anw6410_fb_win0 = { - /* this is to ensure we use win0 */ - .win_mode = { - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode anw6410_lcd_timing = { + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, }; /* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */ static struct s3c_fb_platdata anw6410_lcd_pdata __initdata = { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &anw6410_lcd_timing, .win[0] = &anw6410_fb_win0, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index e20bf5835365..c1ef57edd42d 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -151,26 +151,29 @@ static struct platform_device crag6410_lcd_powerdev = { /* 640x480 URT */ static struct s3c_fb_pd_win crag6410_fb_win0 = { - /* this is to ensure we use win0 */ - .win_mode = { - .left_margin = 150, - .right_margin = 80, - .upper_margin = 40, - .lower_margin = 5, - .hsync_len = 40, - .vsync_len = 5, - .xres = 640, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 640, + .yres = 480, .virtual_y = 480 * 2, .virtual_x = 640, }; +static struct fb_videomode crag6410_lcd_timing = { + .left_margin = 150, + .right_margin = 80, + .upper_margin = 40, + .lower_margin = 5, + .hsync_len = 40, + .vsync_len = 5, + .xres = 640, + .yres = 480, +}; + /* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */ static struct s3c_fb_platdata crag6410_lcd_pdata __initdata = { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &crag6410_lcd_timing, .win[0] = &crag6410_fb_win0, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, diff --git a/arch/arm/mach-s3c64xx/mach-hmt.c b/arch/arm/mach-s3c64xx/mach-hmt.c index 521e07b8501b..4e9b1acefb07 100644 --- a/arch/arm/mach-s3c64xx/mach-hmt.c +++ b/arch/arm/mach-s3c64xx/mach-hmt.c @@ -129,23 +129,27 @@ static struct platform_device hmt_backlight_device = { }; static struct s3c_fb_pd_win hmt_fb_win0 = { - .win_mode = { - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode hmt_lcd_timing = { + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, }; /* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */ static struct s3c_fb_platdata hmt_lcd_pdata __initdata = { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &hmt_lcd_timing, .win[0] = &hmt_fb_win0, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, diff --git a/arch/arm/mach-s3c64xx/mach-mini6410.c b/arch/arm/mach-s3c64xx/mach-mini6410.c index 34f51950b46e..2f425d5d8af5 100644 --- a/arch/arm/mach-s3c64xx/mach-mini6410.c +++ b/arch/arm/mach-s3c64xx/mach-mini6410.c @@ -152,43 +152,53 @@ static struct s3c2410_platform_nand mini6410_nand_info = { }; static struct s3c_fb_pd_win mini6410_lcd_type0_fb_win = { - .win_mode = { /* 4.3" 480x272 */ - .left_margin = 3, - .right_margin = 2, - .upper_margin = 1, - .lower_margin = 1, - .hsync_len = 40, - .vsync_len = 1, - .xres = 480, - .yres = 272, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 480, + .yres = 272, +}; + +static struct fb_videomode mini6410_lcd_type0_timing = { + /* 4.3" 480x272 */ + .left_margin = 3, + .right_margin = 2, + .upper_margin = 1, + .lower_margin = 1, + .hsync_len = 40, + .vsync_len = 1, + .xres = 480, + .yres = 272, }; static struct s3c_fb_pd_win mini6410_lcd_type1_fb_win = { - .win_mode = { /* 7.0" 800x480 */ - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode mini6410_lcd_type1_timing = { + /* 7.0" 800x480 */ + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, }; static struct s3c_fb_platdata mini6410_lcd_pdata[] __initdata = { { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &mini6410_lcd_type0_timing, .win[0] = &mini6410_lcd_type0_fb_win, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, }, { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &mini6410_lcd_type1_timing, .win[0] = &mini6410_lcd_type1_fb_win, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, @@ -316,8 +326,8 @@ static void __init mini6410_machine_init(void) mini6410_parse_features(&features, mini6410_features_str); printk(KERN_INFO "MINI6410: selected LCD display is %dx%d\n", - mini6410_lcd_pdata[features.lcd_index].win[0]->win_mode.xres, - mini6410_lcd_pdata[features.lcd_index].win[0]->win_mode.yres); + mini6410_lcd_pdata[features.lcd_index].win[0]->xres, + mini6410_lcd_pdata[features.lcd_index].win[0]->yres); s3c_nand_set_platdata(&mini6410_nand_info); s3c_fb_set_platdata(&mini6410_lcd_pdata[features.lcd_index]); diff --git a/arch/arm/mach-s3c64xx/mach-real6410.c b/arch/arm/mach-s3c64xx/mach-real6410.c index 21f91e5b093d..0fbd32c74ee5 100644 --- a/arch/arm/mach-s3c64xx/mach-real6410.c +++ b/arch/arm/mach-s3c64xx/mach-real6410.c @@ -118,43 +118,51 @@ static struct platform_device real6410_device_eth = { }; static struct s3c_fb_pd_win real6410_lcd_type0_fb_win = { - .win_mode = { /* 4.3" 480x272 */ - .left_margin = 3, - .right_margin = 2, - .upper_margin = 1, - .lower_margin = 1, - .hsync_len = 40, - .vsync_len = 1, - .xres = 480, - .yres = 272, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 480, + .yres = 272, +}; + +static struct fb_videomode real6410_lcd_type0_timing = { + /* 4.3" 480x272 */ + .left_margin = 3, + .right_margin = 2, + .upper_margin = 1, + .lower_margin = 1, + .hsync_len = 40, + .vsync_len = 1, }; static struct s3c_fb_pd_win real6410_lcd_type1_fb_win = { - .win_mode = { /* 7.0" 800x480 */ - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode real6410_lcd_type1_timing = { + /* 7.0" 800x480 */ + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, }; static struct s3c_fb_platdata real6410_lcd_pdata[] __initdata = { { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &real6410_lcd_type0_timing, .win[0] = &real6410_lcd_type0_fb_win, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, }, { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &real6410_lcd_type1_timing, .win[0] = &real6410_lcd_type1_fb_win, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, @@ -297,8 +305,8 @@ static void __init real6410_machine_init(void) real6410_parse_features(&features, real6410_features_str); printk(KERN_INFO "REAL6410: selected LCD display is %dx%d\n", - real6410_lcd_pdata[features.lcd_index].win[0]->win_mode.xres, - real6410_lcd_pdata[features.lcd_index].win[0]->win_mode.yres); + real6410_lcd_pdata[features.lcd_index].win[0]->xres, + real6410_lcd_pdata[features.lcd_index].win[0]->yres); s3c_fb_set_platdata(&real6410_lcd_pdata[features.lcd_index]); s3c_nand_set_platdata(&real6410_nand_info); diff --git a/arch/arm/mach-s3c64xx/mach-smartq5.c b/arch/arm/mach-s3c64xx/mach-smartq5.c index 3f42431d4dda..03a2f88aabe1 100644 --- a/arch/arm/mach-s3c64xx/mach-smartq5.c +++ b/arch/arm/mach-s3c64xx/mach-smartq5.c @@ -108,23 +108,27 @@ static struct platform_device smartq5_buttons_device = { }; static struct s3c_fb_pd_win smartq5_fb_win0 = { - .win_mode = { - .left_margin = 216, - .right_margin = 40, - .upper_margin = 35, - .lower_margin = 10, - .hsync_len = 1, - .vsync_len = 1, - .xres = 800, - .yres = 480, - .refresh = 80, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode smartq5_lcd_timing = { + .left_margin = 216, + .right_margin = 40, + .upper_margin = 35, + .lower_margin = 10, + .hsync_len = 1, + .vsync_len = 1, + .xres = 800, + .yres = 480, + .refresh = 80, }; static struct s3c_fb_platdata smartq5_lcd_pdata __initdata = { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &smartq5_lcd_timing, .win[0] = &smartq5_fb_win0, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC | diff --git a/arch/arm/mach-s3c64xx/mach-smartq7.c b/arch/arm/mach-s3c64xx/mach-smartq7.c index e5c09b6db967..4e3b0385e868 100644 --- a/arch/arm/mach-s3c64xx/mach-smartq7.c +++ b/arch/arm/mach-s3c64xx/mach-smartq7.c @@ -124,23 +124,27 @@ static struct platform_device smartq7_buttons_device = { }; static struct s3c_fb_pd_win smartq7_fb_win0 = { - .win_mode = { - .left_margin = 3, - .right_margin = 5, - .upper_margin = 1, - .lower_margin = 20, - .hsync_len = 10, - .vsync_len = 3, - .xres = 800, - .yres = 480, - .refresh = 80, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode smartq7_lcd_timing = { + .left_margin = 3, + .right_margin = 5, + .upper_margin = 1, + .lower_margin = 20, + .hsync_len = 10, + .vsync_len = 3, + .xres = 800, + .yres = 480, + .refresh = 80, }; static struct s3c_fb_platdata smartq7_lcd_pdata __initdata = { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &smartq7_lcd_timing, .win[0] = &smartq7_fb_win0, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC | diff --git a/arch/arm/mach-s3c64xx/mach-smdk6410.c b/arch/arm/mach-s3c64xx/mach-smdk6410.c index d55bc96d9582..3cfc90f7ed31 100644 --- a/arch/arm/mach-s3c64xx/mach-smdk6410.c +++ b/arch/arm/mach-s3c64xx/mach-smdk6410.c @@ -146,26 +146,29 @@ static struct platform_device smdk6410_lcd_powerdev = { }; static struct s3c_fb_pd_win smdk6410_fb_win0 = { - /* this is to ensure we use win0 */ - .win_mode = { - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 800, + .yres = 480, .virtual_y = 480 * 2, .virtual_x = 800, }; +static struct fb_videomode smdk6410_lcd_timing = { + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, +}; + /* 405566 clocks per frame => 60Hz refresh requires 24333960Hz clock */ static struct s3c_fb_platdata smdk6410_lcd_pdata __initdata = { .setup_gpio = s3c64xx_fb_gpio_setup_24bpp, + .vtiming = &smdk6410_lcd_timing, .win[0] = &smdk6410_fb_win0, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, diff --git a/arch/arm/mach-s5p64x0/mach-smdk6440.c b/arch/arm/mach-s5p64x0/mach-smdk6440.c index a40e325d62c8..92fefad505cc 100644 --- a/arch/arm/mach-s5p64x0/mach-smdk6440.c +++ b/arch/arm/mach-s5p64x0/mach-smdk6440.c @@ -103,22 +103,26 @@ static struct s3c2410_uartcfg smdk6440_uartcfgs[] __initdata = { /* Frame Buffer */ static struct s3c_fb_pd_win smdk6440_fb_win0 = { - .win_mode = { - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 24, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode smdk6440_lcd_timing = { + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, }; static struct s3c_fb_platdata smdk6440_lcd_pdata __initdata = { .win[0] = &smdk6440_fb_win0, + .vtiming = &smdk6440_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, .setup_gpio = s5p64x0_fb_gpio_setup_24bpp, diff --git a/arch/arm/mach-s5p64x0/mach-smdk6450.c b/arch/arm/mach-s5p64x0/mach-smdk6450.c index efb69e2f2afe..e2335ecf6eae 100644 --- a/arch/arm/mach-s5p64x0/mach-smdk6450.c +++ b/arch/arm/mach-s5p64x0/mach-smdk6450.c @@ -121,22 +121,26 @@ static struct s3c2410_uartcfg smdk6450_uartcfgs[] __initdata = { /* Frame Buffer */ static struct s3c_fb_pd_win smdk6450_fb_win0 = { - .win_mode = { - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 24, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode smdk6450_lcd_timing = { + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, }; static struct s3c_fb_platdata smdk6450_lcd_pdata __initdata = { .win[0] = &smdk6450_fb_win0, + .vtiming = &smdk6450_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, .setup_gpio = s5p64x0_fb_gpio_setup_24bpp, diff --git a/arch/arm/mach-s5pc100/mach-smdkc100.c b/arch/arm/mach-s5pc100/mach-smdkc100.c index 674d22992f3c..0c3ae38d27ca 100644 --- a/arch/arm/mach-s5pc100/mach-smdkc100.c +++ b/arch/arm/mach-s5pc100/mach-smdkc100.c @@ -136,24 +136,27 @@ static struct platform_device smdkc100_lcd_powerdev = { /* Frame Buffer */ static struct s3c_fb_pd_win smdkc100_fb_win0 = { - /* this is to ensure we use win0 */ - .win_mode = { - .left_margin = 8, - .right_margin = 13, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - .refresh = 80, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode smdkc100_lcd_timing = { + .left_margin = 8, + .right_margin = 13, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, + .refresh = 80, }; static struct s3c_fb_platdata smdkc100_lcd_pdata __initdata = { .win[0] = &smdkc100_fb_win0, + .vtiming = &smdkc100_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, .setup_gpio = s5pc100_fb_gpio_setup_24bpp, diff --git a/arch/arm/mach-s5pv210/mach-aquila.c b/arch/arm/mach-s5pv210/mach-aquila.c index a9ea64e0da0d..7b91bbf887fe 100644 --- a/arch/arm/mach-s5pv210/mach-aquila.c +++ b/arch/arm/mach-s5pv210/mach-aquila.c @@ -96,38 +96,34 @@ static struct s3c2410_uartcfg aquila_uartcfgs[] __initdata = { /* Frame Buffer */ static struct s3c_fb_pd_win aquila_fb_win0 = { - .win_mode = { - .left_margin = 16, - .right_margin = 16, - .upper_margin = 3, - .lower_margin = 28, - .hsync_len = 2, - .vsync_len = 2, - .xres = 480, - .yres = 800, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 480, + .yres = 800, }; static struct s3c_fb_pd_win aquila_fb_win1 = { - .win_mode = { - .left_margin = 16, - .right_margin = 16, - .upper_margin = 3, - .lower_margin = 28, - .hsync_len = 2, - .vsync_len = 2, - .xres = 480, - .yres = 800, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 480, + .yres = 800, +}; + +static struct fb_videomode aquila_lcd_timing = { + .left_margin = 16, + .right_margin = 16, + .upper_margin = 3, + .lower_margin = 28, + .hsync_len = 2, + .vsync_len = 2, + .xres = 480, + .yres = 800, }; static struct s3c_fb_platdata aquila_lcd_pdata __initdata = { .win[0] = &aquila_fb_win0, .win[1] = &aquila_fb_win1, + .vtiming = &aquila_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC | VIDCON1_INV_VCLK | VIDCON1_INV_VDEN, diff --git a/arch/arm/mach-s5pv210/mach-goni.c b/arch/arm/mach-s5pv210/mach-goni.c index 2cf5ed75f390..07a840de9a38 100644 --- a/arch/arm/mach-s5pv210/mach-goni.c +++ b/arch/arm/mach-s5pv210/mach-goni.c @@ -105,25 +105,29 @@ static struct s3c2410_uartcfg goni_uartcfgs[] __initdata = { /* Frame Buffer */ static struct s3c_fb_pd_win goni_fb_win0 = { - .win_mode = { - .left_margin = 16, - .right_margin = 16, - .upper_margin = 2, - .lower_margin = 28, - .hsync_len = 2, - .vsync_len = 1, - .xres = 480, - .yres = 800, - .refresh = 55, - }, .max_bpp = 32, .default_bpp = 16, + .xres = 480, + .yres = 800, .virtual_x = 480, .virtual_y = 2 * 800, }; +static struct fb_videomode goni_lcd_timing = { + .left_margin = 16, + .right_margin = 16, + .upper_margin = 2, + .lower_margin = 28, + .hsync_len = 2, + .vsync_len = 1, + .xres = 480, + .yres = 800, + .refresh = 55, +}; + static struct s3c_fb_platdata goni_lcd_pdata __initdata = { .win[0] = &goni_fb_win0, + .vtiming = &goni_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB | VIDCON0_CLKSEL_LCD, .vidcon1 = VIDCON1_INV_VCLK | VIDCON1_INV_VDEN diff --git a/arch/arm/mach-s5pv210/mach-smdkv210.c b/arch/arm/mach-s5pv210/mach-smdkv210.c index 91d4ad8bcc73..5e0c9559d4b3 100644 --- a/arch/arm/mach-s5pv210/mach-smdkv210.c +++ b/arch/arm/mach-s5pv210/mach-smdkv210.c @@ -189,22 +189,26 @@ static struct platform_device smdkv210_lcd_lte480wv = { }; static struct s3c_fb_pd_win smdkv210_fb_win0 = { - .win_mode = { - .left_margin = 13, - .right_margin = 8, - .upper_margin = 7, - .lower_margin = 5, - .hsync_len = 3, - .vsync_len = 1, - .xres = 800, - .yres = 480, - }, .max_bpp = 32, .default_bpp = 24, + .xres = 800, + .yres = 480, +}; + +static struct fb_videomode smdkv210_lcd_timing = { + .left_margin = 13, + .right_margin = 8, + .upper_margin = 7, + .lower_margin = 5, + .hsync_len = 3, + .vsync_len = 1, + .xres = 800, + .yres = 480, }; static struct s3c_fb_platdata smdkv210_lcd0_pdata __initdata = { .win[0] = &smdkv210_fb_win0, + .vtiming = &smdkv210_lcd_timing, .vidcon0 = VIDCON0_VIDOUT_RGB | VIDCON0_PNRMODE_RGB, .vidcon1 = VIDCON1_INV_HSYNC | VIDCON1_INV_VSYNC, .setup_gpio = s5pv210_fb_gpio_setup_24bpp, -- cgit v1.2.3 From 14f087d839d9bd3f90ad69d4b3fde7d236c156b1 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 10 Apr 2012 12:39:55 +0900 Subject: sh: kgdb: Fix up basic SMP support. kgdb needs a kgdb_roundup_cpus() definition in the architecture backend, so just copy over the MIPS version, which already does what we want. Signed-off-by: Paul Mundt --- arch/sh/kernel/kgdb.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'arch') diff --git a/arch/sh/kernel/kgdb.c b/arch/sh/kernel/kgdb.c index b117781bfea2..93458880acad 100644 --- a/arch/sh/kernel/kgdb.c +++ b/arch/sh/kernel/kgdb.c @@ -264,6 +264,18 @@ BUILD_TRAP_HANDLER(singlestep) local_irq_restore(flags); } +static void kgdb_call_nmi_hook(void *ignored) +{ + kgdb_nmicallback(raw_smp_processor_id(), NULL); +} + +void kgdb_roundup_cpus(unsigned long flags) +{ + local_irq_enable(); + smp_call_function(kgdb_call_nmi_hook, NULL, 0); + local_irq_disable(); +} + static int __kgdb_notify(struct die_args *args, unsigned long cmd) { int ret; -- cgit v1.2.3 From fd03e81812a8fb6121773226a4e0c702926077ae Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 10 Apr 2012 13:42:56 +0900 Subject: sh: kgdb: Individual register get/set support. This updates sh following the generic kgdb changes adding support for individual register get/set for kgdb/kdb use. Signed-off-by: Paul Mundt --- arch/sh/include/asm/kgdb.h | 19 ++++--------- arch/sh/kernel/kgdb.c | 70 +++++++++++++++++++++++++++++++--------------- 2 files changed, 53 insertions(+), 36 deletions(-) (limited to 'arch') diff --git a/arch/sh/include/asm/kgdb.h b/arch/sh/include/asm/kgdb.h index f3613952d1ae..00485198f598 100644 --- a/arch/sh/include/asm/kgdb.h +++ b/arch/sh/include/asm/kgdb.h @@ -4,18 +4,6 @@ #include #include -/* Same as pt_regs but has vbr in place of syscall_nr */ -struct kgdb_regs { - unsigned long regs[16]; - unsigned long pc; - unsigned long pr; - unsigned long sr; - unsigned long gbr; - unsigned long mach; - unsigned long macl; - unsigned long vbr; -}; - enum regnames { GDB_R0, GDB_R1, GDB_R2, GDB_R3, GDB_R4, GDB_R5, GDB_R6, GDB_R7, GDB_R8, GDB_R9, GDB_R10, GDB_R11, GDB_R12, GDB_R13, GDB_R14, GDB_R15, @@ -23,7 +11,12 @@ enum regnames { GDB_PC, GDB_PR, GDB_SR, GDB_GBR, GDB_MACH, GDB_MACL, GDB_VBR, }; -#define NUMREGBYTES ((GDB_VBR + 1) * 4) +#define _GP_REGS 16 +#define _EXTRA_REGS 7 +#define GDB_SIZEOF_REG sizeof(u32) + +#define DBG_MAX_REG_NUM (_GP_REGS + _EXTRA_REGS) +#define NUMREGBYTES (DBG_MAX_REG_NUM * sizeof(GDB_SIZEOF_REG)) static inline void arch_kgdb_breakpoint(void) { diff --git a/arch/sh/kernel/kgdb.c b/arch/sh/kernel/kgdb.c index 93458880acad..d25b5ed68b24 100644 --- a/arch/sh/kernel/kgdb.c +++ b/arch/sh/kernel/kgdb.c @@ -1,7 +1,7 @@ /* * SuperH KGDB support * - * Copyright (C) 2008 - 2009 Paul Mundt + * Copyright (C) 2008 - 2012 Paul Mundt * * Single stepping taken from the old stub by Henry Bell and Jeremy Siegel. * @@ -164,36 +164,60 @@ static void undo_single_step(struct pt_regs *linux_regs) stepped_opcode = 0; } -void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) -{ - int i; +struct dbg_reg_def_t dbg_reg_def[DBG_MAX_REG_NUM] = { + { "r0", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[0]) }, + { "r1", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[1]) }, + { "r2", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[2]) }, + { "r3", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[3]) }, + { "r4", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[4]) }, + { "r5", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[5]) }, + { "r6", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[6]) }, + { "r7", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[7]) }, + { "r8", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[8]) }, + { "r9", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[9]) }, + { "r10", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[10]) }, + { "r11", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[11]) }, + { "r12", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[12]) }, + { "r13", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[13]) }, + { "r14", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[14]) }, + { "r15", GDB_SIZEOF_REG, offsetof(struct pt_regs, regs[15]) }, + { "pc", GDB_SIZEOF_REG, offsetof(struct pt_regs, pc) }, + { "pr", GDB_SIZEOF_REG, offsetof(struct pt_regs, pr) }, + { "sr", GDB_SIZEOF_REG, offsetof(struct pt_regs, sr) }, + { "gbr", GDB_SIZEOF_REG, offsetof(struct pt_regs, gbr) }, + { "mach", GDB_SIZEOF_REG, offsetof(struct pt_regs, mach) }, + { "macl", GDB_SIZEOF_REG, offsetof(struct pt_regs, macl) }, + { "vbr", GDB_SIZEOF_REG, -1 }, +}; - for (i = 0; i < 16; i++) - gdb_regs[GDB_R0 + i] = regs->regs[i]; +int dbg_set_reg(int regno, void *mem, struct pt_regs *regs) +{ + if (regno < 0 || regno >= DBG_MAX_REG_NUM) + return -EINVAL; - gdb_regs[GDB_PC] = regs->pc; - gdb_regs[GDB_PR] = regs->pr; - gdb_regs[GDB_SR] = regs->sr; - gdb_regs[GDB_GBR] = regs->gbr; - gdb_regs[GDB_MACH] = regs->mach; - gdb_regs[GDB_MACL] = regs->macl; + if (dbg_reg_def[regno].offset != -1) + memcpy((void *)regs + dbg_reg_def[regno].offset, mem, + dbg_reg_def[regno].size); - __asm__ __volatile__ ("stc vbr, %0" : "=r" (gdb_regs[GDB_VBR])); + return 0; } -void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) +char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) { - int i; + if (regno >= DBG_MAX_REG_NUM || regno < 0) + return NULL; - for (i = 0; i < 16; i++) - regs->regs[GDB_R0 + i] = gdb_regs[GDB_R0 + i]; + if (dbg_reg_def[regno].size != -1) + memcpy(mem, (void *)regs + dbg_reg_def[regno].offset, + dbg_reg_def[regno].size); + + switch (regno) { + case GDB_VBR: + __asm__ __volatile__ ("stc vbr, %0" : "=r" (mem)); + break; + } - regs->pc = gdb_regs[GDB_PC]; - regs->pr = gdb_regs[GDB_PR]; - regs->sr = gdb_regs[GDB_SR]; - regs->gbr = gdb_regs[GDB_GBR]; - regs->mach = gdb_regs[GDB_MACH]; - regs->macl = gdb_regs[GDB_MACL]; + return dbg_reg_def[regno].name; } void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) -- cgit v1.2.3 From 10c5e4e137dc97e54cabd62a6109988ff8670faa Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 10 Apr 2012 14:00:30 +0900 Subject: sh: kgdb: Fill out sleeping_thread_to_gdb_regs() state. Presently we're using a pretty dumbed-down implementation that copies over register state visible from the thread info, leaving the bulk of the switch_to state uncopied. Given that we're also depending on register bank toggling for switch_to optimization we ought to also explicitly zero out the GP regs that reside in an alternate bank in order to prevent handing back garbage. There are a few extra registers that we have state for in switch_to, so copy those over while we're at it. Signed-off-by: Paul Mundt --- arch/sh/kernel/kgdb.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'arch') diff --git a/arch/sh/kernel/kgdb.c b/arch/sh/kernel/kgdb.c index d25b5ed68b24..ba8e76325d12 100644 --- a/arch/sh/kernel/kgdb.c +++ b/arch/sh/kernel/kgdb.c @@ -222,8 +222,31 @@ char *dbg_get_reg(int regno, void *mem, struct pt_regs *regs) void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) { + struct pt_regs *thread_regs = task_pt_regs(p); + int reg; + + /* Initialize to zero */ + for (reg = 0; reg < DBG_MAX_REG_NUM; reg++) + gdb_regs[reg] = 0; + + /* + * Copy out GP regs 8 to 14. + * + * switch_to() relies on SR.RB toggling, so regs 0->7 are banked + * and need privileged instructions to get to. The r15 value we + * fetch from the thread info directly. + */ + for (reg = GDB_R8; reg < GDB_R15; reg++) + gdb_regs[reg] = thread_regs->regs[reg]; + gdb_regs[GDB_R15] = p->thread.sp; gdb_regs[GDB_PC] = p->thread.pc; + + /* + * Additional registers we have context for + */ + gdb_regs[GDB_PR] = thread_regs->pr; + gdb_regs[GDB_GBR] = thread_regs->gbr; } int kgdb_arch_handle_exception(int e_vector, int signo, int err_code, -- cgit v1.2.3 From fd34ef9bc44b87d746b7178e9c4ba51163b46884 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 10 Apr 2012 14:22:39 +0900 Subject: sh: kgdb: Fix up NULL pointer deref by kgdb_nmicallback. kgdb_nmicallback expects valid register state, so just fetch the register state with get_irq_regs() as on other platforms. Signed-off-by: Paul Mundt --- arch/sh/kernel/kgdb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/sh/kernel/kgdb.c b/arch/sh/kernel/kgdb.c index ba8e76325d12..38b313909ac9 100644 --- a/arch/sh/kernel/kgdb.c +++ b/arch/sh/kernel/kgdb.c @@ -313,7 +313,7 @@ BUILD_TRAP_HANDLER(singlestep) static void kgdb_call_nmi_hook(void *ignored) { - kgdb_nmicallback(raw_smp_processor_id(), NULL); + kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs()); } void kgdb_roundup_cpus(unsigned long flags) -- cgit v1.2.3 From 80881dae52d05d3d6c920624157d68006390f01e Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Mon, 26 Mar 2012 12:49:57 -0600 Subject: ARM: tegra: uncompress.h: Implement TEGRA_DEBUG_UART_AUTO_ODMDATA Tegra has 5 UARTS which could be used for low-level debug output. Commit fe26398 "ARM: tegra: uncompress.h: Choose a UART at runtime" implemented one method for the kernel to automatically determine which of these to use at run-time, so that the same DEBUG_LL-enabled kernel image could be used across multiple Tegra boards. The required bootloader-side setup for that option is implemented in NVIDIA's various downstream U-Boot branches, but the U-Boot maintainers have refused to accept it upstream. This change implements an alternative automatic UART selection option using ODMDATA. This is a 32-bit value programmed into Tegra's boot memory which provides a few pieces of basic board-specific information, including a field that indicates the console UART. Setting up this value is part of the standard Tegra boot architecture, and so requires no Tegra-specific hacks in the bootloader's UART driver. Note that in theory, the format of ODMDATA is board-specific. However, in practice all boards use the same location/size/values for the UART field. ODMDATA[19:18] (which drive the type of debug console) is more problematic, since some boards use value 2 for UART and others use 3. This patch just accepts either value; if this doesn't work well for a given board, I'd suggest simply not enabling this debug option when building for that board. Note that the kernel assumes the bootloader has already set up any required pinmux settings for the UART; there is no way the kernel can do this for itself prior to knowing which board it's running on. In practice, people using this feature are highly likely to be using bootloaders that have indeed configured the pinmux. This assumption existed prior to this patch. Signed-off-by: Stephen Warren --- arch/arm/mach-tegra/Kconfig | 29 ++++- arch/arm/mach-tegra/include/mach/uncompress.h | 176 ++++++++++++++++++-------- 2 files changed, 153 insertions(+), 52 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index d0f2546706ca..204d3d4d7388 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -111,7 +111,7 @@ config MACH_VENTANA Support for the nVidia Ventana development platform choice - prompt "Low-level debug console UART" + prompt "Default low-level debug console UART" default TEGRA_DEBUG_UART_NONE config TEGRA_DEBUG_UART_NONE @@ -134,6 +134,33 @@ config TEGRA_DEBUG_UARTE endchoice +choice + prompt "Automatic low-level debug console UART" + default TEGRA_DEBUG_UART_AUTO_NONE + +config TEGRA_DEBUG_UART_AUTO_NONE + bool "None" + +config TEGRA_DEBUG_UART_AUTO_ODMDATA + bool "Via ODMDATA" + help + Automatically determines which UART to use for low-level debug based + on the ODMDATA value. This value is part of the BCT, and is written + to the boot memory device using nvflash, or other flashing tool. + When bits 19:18 are 3, then bits 17:15 indicate which UART to use; + 0/1/2/3/4 are UART A/B/C/D/E. + +config TEGRA_DEBUG_UART_AUTO_SCRATCH + bool "Via UART scratch register" + help + Automatically determines which UART to use for low-level debug based + on the UART scratch register value. Some bootloaders put ASCII 'D' + in this register when they initialize their own console UART output. + Using this option allows the kernel to automatically pick the same + UART. + +endchoice + config TEGRA_SYSTEM_DMA bool "Enable system DMA driver for NVIDIA Tegra SoCs" default y diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h index 5a440f315e57..937c4c50219e 100644 --- a/arch/arm/mach-tegra/include/mach/uncompress.h +++ b/arch/arm/mach-tegra/include/mach/uncompress.h @@ -63,52 +63,86 @@ static inline void save_uart_address(void) buf[0] = 0; } -/* - * Setup before decompression. This is where we do UART selection for - * earlyprintk and init the uart_base register. - */ -static inline void arch_decomp_setup(void) +static const struct { + u32 base; + u32 reset_reg; + u32 clock_reg; + u32 bit; +} uarts[] = { + { + TEGRA_UARTA_BASE, + TEGRA_CLK_RESET_BASE + 0x04, + TEGRA_CLK_RESET_BASE + 0x10, + 6, + }, + { + TEGRA_UARTB_BASE, + TEGRA_CLK_RESET_BASE + 0x04, + TEGRA_CLK_RESET_BASE + 0x10, + 7, + }, + { + TEGRA_UARTC_BASE, + TEGRA_CLK_RESET_BASE + 0x08, + TEGRA_CLK_RESET_BASE + 0x14, + 23, + }, + { + TEGRA_UARTD_BASE, + TEGRA_CLK_RESET_BASE + 0x0c, + TEGRA_CLK_RESET_BASE + 0x18, + 1, + }, + { + TEGRA_UARTE_BASE, + TEGRA_CLK_RESET_BASE + 0x0c, + TEGRA_CLK_RESET_BASE + 0x18, + 2, + }, +}; + +static inline bool uart_clocked(int i) +{ + if (*(u8 *)uarts[i].reset_reg & BIT(uarts[i].bit)) + return false; + + if (!(*(u8 *)uarts[i].clock_reg & BIT(uarts[i].bit))) + return false; + + return true; +} + +#ifdef CONFIG_TEGRA_DEBUG_UART_AUTO_ODMDATA +int auto_odmdata(void) +{ + volatile u32 *pmc = (volatile u32 *)TEGRA_PMC_BASE; + u32 odmdata = pmc[0xa0 / 4]; + + /* + * Bits 19:18 are the console type: 0=default, 1=none, 2==DCC, 3==UART + * Some boards apparently swap the last two values, but we don't have + * any way of catering for that here, so we just accept either. If this + * doesn't make sense for your board, just don't enable this feature. + * + * Bits 17:15 indicate the UART to use, 0/1/2/3/4 are UART A/B/C/D/E. + */ + + switch ((odmdata >> 18) & 3) { + case 2: + case 3: + break; + default: + return -1; + } + + return (odmdata >> 15) & 7; +} +#endif + +#ifdef CONFIG_TEGRA_DEBUG_UART_AUTO_SCRATCH +int auto_scratch(void) { - static const struct { - u32 base; - u32 reset_reg; - u32 clock_reg; - u32 bit; - } uarts[] = { - { - TEGRA_UARTA_BASE, - TEGRA_CLK_RESET_BASE + 0x04, - TEGRA_CLK_RESET_BASE + 0x10, - 6, - }, - { - TEGRA_UARTB_BASE, - TEGRA_CLK_RESET_BASE + 0x04, - TEGRA_CLK_RESET_BASE + 0x10, - 7, - }, - { - TEGRA_UARTC_BASE, - TEGRA_CLK_RESET_BASE + 0x08, - TEGRA_CLK_RESET_BASE + 0x14, - 23, - }, - { - TEGRA_UARTD_BASE, - TEGRA_CLK_RESET_BASE + 0x0c, - TEGRA_CLK_RESET_BASE + 0x18, - 1, - }, - { - TEGRA_UARTE_BASE, - TEGRA_CLK_RESET_BASE + 0x0c, - TEGRA_CLK_RESET_BASE + 0x18, - 2, - }, - }; int i; - volatile u32 *apb_misc = (volatile u32 *)TEGRA_APB_MISC_BASE; - u32 chip, div; /* * Look for the first UART that: @@ -125,20 +159,60 @@ static inline void arch_decomp_setup(void) * back to what's specified in TEGRA_DEBUG_UART_BASE. */ for (i = 0; i < ARRAY_SIZE(uarts); i++) { - if (*(u8 *)uarts[i].reset_reg & BIT(uarts[i].bit)) - continue; - - if (!(*(u8 *)uarts[i].clock_reg & BIT(uarts[i].bit))) + if (!uart_clocked(i)) continue; uart = (volatile u8 *)uarts[i].base; if (uart[UART_SCR << DEBUG_UART_SHIFT] != 'D') continue; - break; + return i; } - if (i == ARRAY_SIZE(uarts)) - uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE; + + return -1; +} +#endif + +/* + * Setup before decompression. This is where we do UART selection for + * earlyprintk and init the uart_base register. + */ +static inline void arch_decomp_setup(void) +{ + int uart_id, auto_uart_id; + volatile u32 *apb_misc = (volatile u32 *)TEGRA_APB_MISC_BASE; + u32 chip, div; + +#if defined(CONFIG_TEGRA_DEBUG_UARTA) + uart_id = 0; +#elif defined(CONFIG_TEGRA_DEBUG_UARTB) + uart_id = 1; +#elif defined(CONFIG_TEGRA_DEBUG_UARTC) + uart_id = 2; +#elif defined(CONFIG_TEGRA_DEBUG_UARTD) + uart_id = 3; +#elif defined(CONFIG_TEGRA_DEBUG_UARTE) + uart_id = 4; +#else + uart_id = -1; +#endif + +#if defined(CONFIG_TEGRA_DEBUG_UART_AUTO_ODMDATA) + auto_uart_id = auto_odmdata(); +#elif defined(CONFIG_TEGRA_DEBUG_UART_AUTO_SCRATCH) + auto_uart_id = auto_scratch(); +#else + auto_uart_id = -1; +#endif + if (auto_uart_id != -1) + uart_id = auto_uart_id; + + if (uart_id < 0 || uart_id >= ARRAY_SIZE(uarts) || + !uart_clocked(uart_id)) + uart = NULL; + else + uart = (volatile u8 *)uarts[uart_id].base; + save_uart_address(); if (uart == NULL) return; -- cgit v1.2.3 From ede55aaa8bb2dd37e302b2f94312306d216e5f97 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 21 Mar 2012 11:35:49 -0700 Subject: arm: ep93xx: use DEFINE_RES_* macros Use the DEFINE_RES_* macros to reduce some of the boilerplate code. Signed-off-by: Hartley Sweeten Acked-by: Mika Westerberg Signed-off-by: Ryan Mallon --- arch/arm/mach-ep93xx/core.c | 94 ++++++++------------------------------------- 1 file changed, 16 insertions(+), 78 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index 8d2589588713..a2cb176cee33 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -241,11 +241,7 @@ unsigned int ep93xx_chip_revision(void) * EP93xx GPIO *************************************************************************/ static struct resource ep93xx_gpio_resource[] = { - { - .start = EP93XX_GPIO_PHYS_BASE, - .end = EP93XX_GPIO_PHYS_BASE + 0xcc - 1, - .flags = IORESOURCE_MEM, - }, + DEFINE_RES_MEM(EP93XX_GPIO_PHYS_BASE, 0xcc), }; static struct platform_device ep93xx_gpio_device = { @@ -288,11 +284,7 @@ static AMBA_APB_DEVICE(uart3, "apb:uart3", 0x00041010, EP93XX_UART3_PHYS_BASE, { IRQ_EP93XX_UART3 }, &ep93xx_uart_data); static struct resource ep93xx_rtc_resource[] = { - { - .start = EP93XX_RTC_PHYS_BASE, - .end = EP93XX_RTC_PHYS_BASE + 0x10c - 1, - .flags = IORESOURCE_MEM, - }, + DEFINE_RES_MEM(EP93XX_RTC_PHYS_BASE, 0x10c), }; static struct platform_device ep93xx_rtc_device = { @@ -304,16 +296,8 @@ static struct platform_device ep93xx_rtc_device = { static struct resource ep93xx_ohci_resources[] = { - [0] = { - .start = EP93XX_USB_PHYS_BASE, - .end = EP93XX_USB_PHYS_BASE + 0x0fff, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = IRQ_EP93XX_USB, - .end = IRQ_EP93XX_USB, - .flags = IORESOURCE_IRQ, - }, + DEFINE_RES_MEM(EP93XX_USB_PHYS_BASE, 0x1000), + DEFINE_RES_IRQ(IRQ_EP93XX_USB), }; @@ -372,15 +356,8 @@ void __init ep93xx_register_flash(unsigned int width, static struct ep93xx_eth_data ep93xx_eth_data; static struct resource ep93xx_eth_resource[] = { - { - .start = EP93XX_ETHERNET_PHYS_BASE, - .end = EP93XX_ETHERNET_PHYS_BASE + 0xffff, - .flags = IORESOURCE_MEM, - }, { - .start = IRQ_EP93XX_ETHERNET, - .end = IRQ_EP93XX_ETHERNET, - .flags = IORESOURCE_IRQ, - } + DEFINE_RES_MEM(EP93XX_ETHERNET_PHYS_BASE, 0x10000), + DEFINE_REQ_IRQ(IRQ_EP93XX_ETHERNET), }; static u64 ep93xx_eth_dma_mask = DMA_BIT_MASK(32); @@ -461,16 +438,8 @@ void __init ep93xx_register_i2c(struct i2c_gpio_platform_data *data, static struct ep93xx_spi_info ep93xx_spi_master_data; static struct resource ep93xx_spi_resources[] = { - { - .start = EP93XX_SPI_PHYS_BASE, - .end = EP93XX_SPI_PHYS_BASE + 0x18 - 1, - .flags = IORESOURCE_MEM, - }, - { - .start = IRQ_EP93XX_SSP, - .end = IRQ_EP93XX_SSP, - .flags = IORESOURCE_IRQ, - }, + DEFINE_RES_MEM(EP93XX_SPI_PHYS_BASE, 0x18), + DEFINE_RES_IRQ(IRQ_EP93XX_SSP), }; static u64 ep93xx_spi_dma_mask = DMA_BIT_MASK(32); @@ -541,11 +510,7 @@ static struct platform_device ep93xx_leds = { * EP93xx pwm peripheral handling *************************************************************************/ static struct resource ep93xx_pwm0_resource[] = { - { - .start = EP93XX_PWM_PHYS_BASE, - .end = EP93XX_PWM_PHYS_BASE + 0x10 - 1, - .flags = IORESOURCE_MEM, - }, + DEFINE_RES_MEM(EP93XX_PWM_PHYS_BASE, 0x10), }; static struct platform_device ep93xx_pwm0_device = { @@ -556,11 +521,7 @@ static struct platform_device ep93xx_pwm0_device = { }; static struct resource ep93xx_pwm1_resource[] = { - { - .start = EP93XX_PWM_PHYS_BASE + 0x20, - .end = EP93XX_PWM_PHYS_BASE + 0x30 - 1, - .flags = IORESOURCE_MEM, - }, + DEFINE_RES_MEM(EP93XX_PWM_PHYS_BASE + 0x20, 0x10), }; static struct platform_device ep93xx_pwm1_device = { @@ -628,11 +589,7 @@ EXPORT_SYMBOL(ep93xx_pwm_release_gpio); static struct ep93xxfb_mach_info ep93xxfb_data; static struct resource ep93xx_fb_resource[] = { - { - .start = EP93XX_RASTER_PHYS_BASE, - .end = EP93XX_RASTER_PHYS_BASE + 0x800 - 1, - .flags = IORESOURCE_MEM, - }, + DEFINE_RES_MEM(EP93XX_RASTER_PHYS_BASE, 0x800), }; static struct platform_device ep93xx_fb_device = { @@ -680,15 +637,8 @@ void __init ep93xx_register_fb(struct ep93xxfb_mach_info *data) static struct ep93xx_keypad_platform_data ep93xx_keypad_data; static struct resource ep93xx_keypad_resource[] = { - { - .start = EP93XX_KEY_MATRIX_PHYS_BASE, - .end = EP93XX_KEY_MATRIX_PHYS_BASE + 0x0c - 1, - .flags = IORESOURCE_MEM, - }, { - .start = IRQ_EP93XX_KEY, - .end = IRQ_EP93XX_KEY, - .flags = IORESOURCE_IRQ, - }, + DEFINE_RES_MEM(EP93XX_KEY_MATRIX_PHYS_BASE, 0x0c), + DEFINE_RES_IRQ(IRQ_EP93XX_KEY), }; static struct platform_device ep93xx_keypad_device = { @@ -761,11 +711,7 @@ EXPORT_SYMBOL(ep93xx_keypad_release_gpio); * EP93xx I2S audio peripheral handling *************************************************************************/ static struct resource ep93xx_i2s_resource[] = { - { - .start = EP93XX_I2S_PHYS_BASE, - .end = EP93XX_I2S_PHYS_BASE + 0x100 - 1, - .flags = IORESOURCE_MEM, - }, + DEFINE_RES_MEM(EP93XX_I2S_PHYS_BASE, 0x100), }; static struct platform_device ep93xx_i2s_device = { @@ -824,16 +770,8 @@ EXPORT_SYMBOL(ep93xx_i2s_release); * EP93xx AC97 audio peripheral handling *************************************************************************/ static struct resource ep93xx_ac97_resources[] = { - { - .start = EP93XX_AAC_PHYS_BASE, - .end = EP93XX_AAC_PHYS_BASE + 0xac - 1, - .flags = IORESOURCE_MEM, - }, - { - .start = IRQ_EP93XX_AACINTR, - .end = IRQ_EP93XX_AACINTR, - .flags = IORESOURCE_IRQ, - }, + DEFINE_RES_MEM(EP93XX_AAC_PHYS_BASE, 0xac), + DEFINE_RES_IRQ(IRQ_EP93XX_AACINTR), }; static struct platform_device ep93xx_ac97_device = { -- cgit v1.2.3 From 21cb20d758254e68a369ba83c721f8ce083e6659 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 11 Apr 2012 10:48:24 +0900 Subject: sh: kgdb: Unset CACHE_FLUSH_IS_SAFE for SMP. Our SMP cache flush ops use CPU cross calls to deal with things like I-cache accesses not being broadcast in hardware, so ensure that the CACHE_FLUSH_IS_SAFE reflects this. Signed-off-by: Paul Mundt --- arch/sh/include/asm/kgdb.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'arch') diff --git a/arch/sh/include/asm/kgdb.h b/arch/sh/include/asm/kgdb.h index 00485198f598..9e7d2d1b03e0 100644 --- a/arch/sh/include/asm/kgdb.h +++ b/arch/sh/include/asm/kgdb.h @@ -23,10 +23,15 @@ static inline void arch_kgdb_breakpoint(void) __asm__ __volatile__ ("trapa #0x3c\n"); } -#define BUFMAX 2048 - -#define CACHE_FLUSH_IS_SAFE 1 #define BREAK_INSTR_SIZE 2 +#define BUFMAX 2048 + +#ifdef CONFIG_SMP +# define CACHE_FLUSH_IS_SAFE 0 +#else +# define CACHE_FLUSH_IS_SAFE 1 +#endif + #define GDB_ADJUSTS_BREAK_OFFSET #endif /* __ASM_SH_KGDB_H */ -- cgit v1.2.3 From 5528a8469fbc01db218fbc00ebec1e3fc088e759 Mon Sep 17 00:00:00 2001 From: Rafal Prylowski Date: Thu, 5 Apr 2012 13:44:07 +0200 Subject: arm: ep93xx: Don't try to release not acquired GPIO lines Fail path of ep93xx_keypad_acquire_gpio() tries to release GPIO lines not acquired successfully before. Fix this. Signed-off-by: Rafal Prylowski Acked-by: H Hartley Sweeten Signed-off-by: Ryan Mallon --- arch/arm/mach-ep93xx/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index 8d2589588713..2ea2ffc61875 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -734,7 +734,7 @@ int ep93xx_keypad_acquire_gpio(struct platform_device *pdev) fail_gpio_d: gpio_free(EP93XX_GPIO_LINE_C(i)); fail_gpio_c: - for ( ; i >= 0; --i) { + for (--i; i >= 0; --i) { gpio_free(EP93XX_GPIO_LINE_C(i)); gpio_free(EP93XX_GPIO_LINE_D(i)); } -- cgit v1.2.3 From 11fd982400a8779cb4b5f7cdc806008569ff545c Mon Sep 17 00:00:00 2001 From: Kautuk Consul Date: Sat, 31 Mar 2012 08:06:11 -0400 Subject: sh/mm/fault_32.c: Port OOM changes to do_page_fault Commit d065bd810b6deb67d4897a14bfe21f8eb526ba99 (mm: retry page fault when blocking on disk transfer) and commit 37b23e0525d393d48a7d59f870b3bc061a30ccdb (x86,mm: make pagefault killable) The above commits introduced changes into the x86 pagefault handler for making the page fault handler retryable as well as killable. These changes reduce the mmap_sem hold time, which is crucial during OOM killer invocation. Port these changes to the 32-bit SH platform. Signed-off-by: Kautuk Consul Signed-off-by: Paul Mundt --- arch/sh/mm/fault_32.c | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) (limited to 'arch') diff --git a/arch/sh/mm/fault_32.c b/arch/sh/mm/fault_32.c index 324eef93c900..98e88a6112ba 100644 --- a/arch/sh/mm/fault_32.c +++ b/arch/sh/mm/fault_32.c @@ -129,6 +129,8 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, int si_code; int fault; siginfo_t info; + unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE | + (writeaccess ? FAULT_FLAG_WRITE : 0)); tsk = current; mm = tsk->mm; @@ -169,6 +171,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, if (in_atomic() || !mm) goto no_context; +retry: down_read(&mm->mmap_sem); vma = find_vma(mm, address); @@ -200,7 +203,11 @@ good_area: * make sure we exit gracefully rather than endlessly redo * the fault. */ - fault = handle_mm_fault(mm, vma, address, writeaccess ? FAULT_FLAG_WRITE : 0); + fault = handle_mm_fault(mm, vma, address, flags); + + if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) + return; + if (unlikely(fault & VM_FAULT_ERROR)) { if (fault & VM_FAULT_OOM) goto out_of_memory; @@ -208,14 +215,27 @@ good_area: goto do_sigbus; BUG(); } - if (fault & VM_FAULT_MAJOR) { - tsk->maj_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, - regs, address); - } else { - tsk->min_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, - regs, address); + + if (flags & FAULT_FLAG_ALLOW_RETRY) { + if (fault & VM_FAULT_MAJOR) { + tsk->maj_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, + regs, address); + } else { + tsk->min_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, + regs, address); + } + if (fault & VM_FAULT_RETRY) { + flags &= ~FAULT_FLAG_ALLOW_RETRY; + + /* + * No need to up_read(&mm->mmap_sem) as we would + * have already released it in __lock_page_or_retry + * in mm/filemap.c. + */ + goto retry; + } } up_read(&mm->mmap_sem); -- cgit v1.2.3 From a1e2030122d4c2605089e60dce28d2fcf9c3ef98 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 11 Apr 2012 12:44:50 +0900 Subject: sh64: Port OOM changes to do_page_fault Reflect the sh32 OOM changes for the sh64 page fault handler, too. Signed-off-by: Paul Mundt --- arch/sh/mm/tlbflush_64.c | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) (limited to 'arch') diff --git a/arch/sh/mm/tlbflush_64.c b/arch/sh/mm/tlbflush_64.c index 11c5a18f10ed..70b3c271aa9f 100644 --- a/arch/sh/mm/tlbflush_64.c +++ b/arch/sh/mm/tlbflush_64.c @@ -3,7 +3,7 @@ * * Copyright (C) 2000, 2001 Paolo Alberelli * Copyright (C) 2003 Richard Curnow (/proc/tlb, bug fixes) - * Copyright (C) 2003 - 2009 Paul Mundt + * Copyright (C) 2003 - 2012 Paul Mundt * * 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 @@ -95,6 +95,8 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess, struct mm_struct *mm; struct vm_area_struct * vma; const struct exception_table_entry *fixup; + unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE | + (writeaccess ? FAULT_FLAG_WRITE : 0)); pte_t *pte; int fault; @@ -124,6 +126,7 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long writeaccess, if (in_atomic() || !mm) goto no_context; +retry: /* TLB misses upon some cache flushes get done under cli() */ down_read(&mm->mmap_sem); @@ -188,7 +191,11 @@ good_area: * make sure we exit gracefully rather than endlessly redo * the fault. */ - fault = handle_mm_fault(mm, vma, address, writeaccess ? FAULT_FLAG_WRITE : 0); + fault = handle_mm_fault(mm, vma, address, flags); + + if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) + return; + if (unlikely(fault & VM_FAULT_ERROR)) { if (fault & VM_FAULT_OOM) goto out_of_memory; @@ -197,14 +204,27 @@ good_area: BUG(); } - if (fault & VM_FAULT_MAJOR) { - tsk->maj_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, - regs, address); - } else { - tsk->min_flt++; - perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, - regs, address); + if (flags & FAULT_FLAG_ALLOW_RETRY) { + if (fault & VM_FAULT_MAJOR) { + tsk->maj_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, + regs, address); + } else { + tsk->min_flt++; + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, + regs, address); + } + + if (fault & VM_FAULT_RETRY) { + flags &= ~FAULT_FLAG_ALLOW_RETRY; + + /* + * No need to up_read(&mm->mmap_sem) as we would + * have already released it in __lock_page_or_retry + * in mm/filemap.c. + */ + goto retry; + } } /* If we get here, the page fault has been handled. Do the TLB refill -- cgit v1.2.3 From ba2a3cdf76c32861c95e9a226b75dbecaec99469 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 11 Apr 2012 12:53:06 +0900 Subject: sh64: Kill off dead page fault debug cruft. In the future we'll be unifying some of the 32/64 page fault path, so start to tidy up the _64 one by killing off some of the unused debug cruft. Signed-off-by: Paul Mundt --- arch/sh/mm/tlbflush_64.c | 74 ++++-------------------------------------------- 1 file changed, 5 insertions(+), 69 deletions(-) (limited to 'arch') diff --git a/arch/sh/mm/tlbflush_64.c b/arch/sh/mm/tlbflush_64.c index 70b3c271aa9f..605dc65dc66d 100644 --- a/arch/sh/mm/tlbflush_64.c +++ b/arch/sh/mm/tlbflush_64.c @@ -28,33 +28,6 @@ #include #include -extern void die(const char *,struct pt_regs *,long); - -#define PFLAG(val,flag) (( (val) & (flag) ) ? #flag : "" ) -#define PPROT(flag) PFLAG(pgprot_val(prot),flag) - -static inline void print_prots(pgprot_t prot) -{ - printk("prot is 0x%016llx\n",pgprot_val(prot)); - - printk("%s %s %s %s %s\n",PPROT(_PAGE_SHARED),PPROT(_PAGE_READ), - PPROT(_PAGE_EXECUTE),PPROT(_PAGE_WRITE),PPROT(_PAGE_USER)); -} - -static inline void print_vma(struct vm_area_struct *vma) -{ - printk("vma start 0x%08lx\n", vma->vm_start); - printk("vma end 0x%08lx\n", vma->vm_end); - - print_prots(vma->vm_page_prot); - printk("vm_flags 0x%08lx\n", vma->vm_flags); -} - -static inline void print_task(struct task_struct *tsk) -{ - printk("Task pid %d\n", task_pid_nr(tsk)); -} - static pte_t *lookup_pte(struct mm_struct *mm, unsigned long address) { pgd_t *dir; @@ -131,43 +104,15 @@ retry: down_read(&mm->mmap_sem); vma = find_vma(mm, address); - - if (!vma) { -#ifdef DEBUG_FAULT - print_task(tsk); - printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n", - __func__, __LINE__, - address,regs->pc,textaccess,writeaccess); - show_regs(regs); -#endif + if (!vma) goto bad_area; - } - if (vma->vm_start <= address) { + if (vma->vm_start <= address) goto good_area; - } - - if (!(vma->vm_flags & VM_GROWSDOWN)) { -#ifdef DEBUG_FAULT - print_task(tsk); - printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n", - __func__, __LINE__, - address,regs->pc,textaccess,writeaccess); - show_regs(regs); - - print_vma(vma); -#endif + if (!(vma->vm_flags & VM_GROWSDOWN)) goto bad_area; - } - if (expand_stack(vma, address)) { -#ifdef DEBUG_FAULT - print_task(tsk); - printk("%s:%d fault, address is 0x%08x PC %016Lx textaccess %d writeaccess %d\n", - __func__, __LINE__, - address,regs->pc,textaccess,writeaccess); - show_regs(regs); -#endif + if (expand_stack(vma, address)) goto bad_area; - } + /* * Ok, we have a good vm_area for this memory access, so * we can handle it.. @@ -251,9 +196,6 @@ no_pte: * Fix it, but check if it's kernel or user first.. */ bad_area: -#ifdef DEBUG_FAULT - printk("fault:bad area\n"); -#endif up_read(&mm->mmap_sem); if (user_mode(regs)) { @@ -266,9 +208,6 @@ bad_area: printk("user mode bad_area address=%08lx pid=%d (%s) pc=%08lx\n", address, task_pid_nr(current), current->comm, (unsigned long) regs->pc); -#if 0 - show_regs(regs); -#endif } if (is_global_init(tsk)) { panic("INIT had user mode bad_area\n"); @@ -283,9 +222,6 @@ bad_area: } no_context: -#ifdef DEBUG_FAULT - printk("fault:No context\n"); -#endif /* Are we prepared to handle this kernel fault? */ fixup = search_exception_tables(regs->pc); if (fixup) { -- cgit v1.2.3 From 0f2c9f20e4e339de30cfd5613dfa9505e7b9c58b Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 28 Mar 2012 19:22:30 +0900 Subject: ARM: mach-shmobile: Introduce shmobile_setup_delay() Add the function shmobile_setup_delay() to let platforms configure their maximum loops per jiffy delay. With this jiffies calculation done the dependency on early timer is removed. In the future this allows us to assign timers using the regular driver model via the device tree. Signed-off-by: Magnus Damm Signed-off-by: Rafael J. Wysocki --- arch/arm/mach-shmobile/include/mach/common.h | 2 ++ arch/arm/mach-shmobile/timer.c | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) (limited to 'arch') diff --git a/arch/arm/mach-shmobile/include/mach/common.h b/arch/arm/mach-shmobile/include/mach/common.h index 83ad3fe0a75f..cc1d73514ffa 100644 --- a/arch/arm/mach-shmobile/include/mach/common.h +++ b/arch/arm/mach-shmobile/include/mach/common.h @@ -3,6 +3,8 @@ extern void shmobile_earlytimer_init(void); extern struct sys_timer shmobile_timer; +extern void shmobile_setup_delay(unsigned int max_cpu_core_mhz, + unsigned int mult, unsigned int div); struct twd_local_timer; void shmobile_twd_init(struct twd_local_timer *twd_local_timer); extern void shmobile_setup_console(void); diff --git a/arch/arm/mach-shmobile/timer.c b/arch/arm/mach-shmobile/timer.c index 2fba5f3d1c8a..599e0081a533 100644 --- a/arch/arm/mach-shmobile/timer.c +++ b/arch/arm/mach-shmobile/timer.c @@ -19,9 +19,26 @@ * */ #include +#include #include #include +void __init shmobile_setup_delay(unsigned int max_cpu_core_mhz, + unsigned int mult, unsigned int div) +{ + /* calculate a worst-case loops-per-jiffy value + * based on maximum cpu core mhz setting and the + * __delay() implementation in arch/arm/lib/delay.S + * + * this will result in a longer delay than expected + * when the cpu core runs on lower frequencies. + */ + + unsigned int value = (1000000 * mult) / (HZ * div); + + lpj_fine = max_cpu_core_mhz * value; +} + static void __init shmobile_late_time_init(void) { /* -- cgit v1.2.3 From d7e7e4ffc64f700e876a9f3b2727febaa2041221 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 28 Mar 2012 19:22:46 +0900 Subject: ARM: mach-shmobile: Introduce INTC_IRQ_PINS_16H Add INTC_IRQ_PINS_16H to allow broken out support of the the high 16 external interrupt pins. On SoCs with 32 external interrupt pins the interrupt vectors for the low 16 and the high 16 interrupt pins are sparesly populated. The low 16 are at 0x0200 and high 16 are at 0x3200 which with current macros results in a separation of 384 linux interrupts. This sparse population makes it unsuitable with a single IRQ domain to cover the full IRQ range, so this macro breaks out the 32 pins into two separate 16 bit controllers to allow two independent INTC instances with two separate IRQ domains. Signed-off-by: Magnus Damm Signed-off-by: Rafael J. Wysocki --- arch/arm/mach-shmobile/include/mach/intc.h | 44 ++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'arch') diff --git a/arch/arm/mach-shmobile/include/mach/intc.h b/arch/arm/mach-shmobile/include/mach/intc.h index 8b22258c8caa..a5603c76cfe0 100644 --- a/arch/arm/mach-shmobile/include/mach/intc.h +++ b/arch/arm/mach-shmobile/include/mach/intc.h @@ -142,6 +142,50 @@ static struct intc_desc p ## _desc __initdata = { \ p ## _sense_registers, p ## _ack_registers) \ } +#define INTC_IRQ_PINS_16H(p, base, vect, str) \ + \ +static struct resource p ## _resources[] __initdata = { \ + [0] = { \ + .start = base, \ + .end = base + 0x64, \ + .flags = IORESOURCE_MEM, \ + }, \ +}; \ + \ +enum { \ + p ## _UNUSED = 0, \ + INTC_IRQ_PINS_ENUM_16H(p), \ +}; \ + \ +static struct intc_vect p ## _vectors[] __initdata = { \ + INTC_IRQ_PINS_VECT_16H(p, vect), \ +}; \ + \ +static struct intc_mask_reg p ## _mask_registers[] __initdata = { \ + INTC_IRQ_PINS_MASK_16H(p, base), \ +}; \ + \ +static struct intc_prio_reg p ## _prio_registers[] __initdata = { \ + INTC_IRQ_PINS_PRIO_16H(p, base), \ +}; \ + \ +static struct intc_sense_reg p ## _sense_registers[] __initdata = { \ + INTC_IRQ_PINS_SENSE_16H(p, base), \ +}; \ + \ +static struct intc_mask_reg p ## _ack_registers[] __initdata = { \ + INTC_IRQ_PINS_ACK_16H(p, base), \ +}; \ + \ +static struct intc_desc p ## _desc __initdata = { \ + .name = str, \ + .resource = p ## _resources, \ + .num_resources = ARRAY_SIZE(p ## _resources), \ + .hw = INTC_HW_DESC(p ## _vectors, NULL, \ + p ## _mask_registers, p ## _prio_registers, \ + p ## _sense_registers, p ## _ack_registers) \ +} + #define INTC_IRQ_PINS_32(p, base, vect, str) \ \ static struct resource p ## _resources[] __initdata = { \ -- cgit v1.2.3 From 1ee8299a9ec1ce5137a044c7768293007b9a3267 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 28 Mar 2012 19:22:54 +0900 Subject: ARM: mach-shmobile: Use 0x3400 as INTCS vector offset Update mach-shmobile to use 0x3400 as INTCS_VECT_BASE. Since the ARM architecture a little while back added support for 10 bit irqs we can now undo the previously merged commit 9b7c23adb350a108737a993c9c781463c1439dc6 and use 0x3400 as INTCS vector base. This change is necessary to avoid overlapping of interrupt ranges so separate IRQ domains can be used for different INTC instances. Without this fix the vectors used by various INTC instances are overlapping on for instance sh7372 which works at the moment but breaks upcoming IRQ domain support. Signed-off-by: Magnus Damm Signed-off-by: Rafael J. Wysocki --- arch/arm/mach-shmobile/include/mach/irqs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/arm/mach-shmobile/include/mach/irqs.h b/arch/arm/mach-shmobile/include/mach/irqs.h index 4e686cc201fc..06a5da3c3050 100644 --- a/arch/arm/mach-shmobile/include/mach/irqs.h +++ b/arch/arm/mach-shmobile/include/mach/irqs.h @@ -7,7 +7,7 @@ #define gic_spi(nr) ((nr) + 32) /* INTCS */ -#define INTCS_VECT_BASE 0x2200 +#define INTCS_VECT_BASE 0x3400 #define INTCS_VECT(n, vect) INTC_VECT((n), INTCS_VECT_BASE + (vect)) #define intcs_evt2irq(evt) evt2irq(INTCS_VECT_BASE + (evt)) -- cgit v1.2.3 From 2ff6530c065814035df505ae8a6ddf461b3441d3 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 28 Mar 2012 19:23:03 +0900 Subject: ARM: mach-shmobile: Use INTC_IRQ_PINS_16H on sh7372 Use INTC_IRQ_PINS_16H on sh7372 to have two separate interrupt controllers for the external IRQ pins. This is preparation work for future IRQ domain support. Signed-off-by: Magnus Damm Signed-off-by: Rafael J. Wysocki --- arch/arm/mach-shmobile/intc-sh7372.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-shmobile/intc-sh7372.c b/arch/arm/mach-shmobile/intc-sh7372.c index 6447e0af52d4..0ba8b7b69f60 100644 --- a/arch/arm/mach-shmobile/intc-sh7372.c +++ b/arch/arm/mach-shmobile/intc-sh7372.c @@ -305,8 +305,12 @@ static DECLARE_INTC_DESC(intca_desc, "sh7372-intca", intca_mask_registers, intca_prio_registers, NULL); -INTC_IRQ_PINS_32(intca_irq_pins, 0xe6900000, - INTC_VECT, "sh7372-intca-irq-pins"); +INTC_IRQ_PINS_16(intca_irq_pins_lo, 0xe6900000, + INTC_VECT, "sh7372-intca-irq-lo"); + +INTC_IRQ_PINS_16H(intca_irq_pins_hi, 0xe6900000, + INTC_VECT, "sh7372-intca-irq-hi"); + enum { UNUSED_INTCS = 0, ENABLED_INTCS, @@ -563,7 +567,8 @@ void __init sh7372_init_irq(void) intcs_ffd5 = ioremap_nocache(0xffd50000, PAGE_SIZE); register_intc_controller(&intca_desc); - register_intc_controller(&intca_irq_pins_desc); + register_intc_controller(&intca_irq_pins_lo_desc); + register_intc_controller(&intca_irq_pins_hi_desc); register_intc_controller(&intcs_desc); /* demux using INTEVTSA */ -- cgit v1.2.3 From 012f825f13d8e5f1007d1f604517dba1b9a5586e Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 28 Mar 2012 19:23:11 +0900 Subject: ARM: mach-shmobile: Rework sh7372 INTCS demuxer V2 This patch is the sh7372 INTC demux rework V2. Updates the sh7372 INTCS demuxer to not hook into the INTCA interrupt controller. The mask register for the chained INTCS interrupt source happens to be located in the INTCS register range instead of the INTCA as expected. To allow each INTCA and INTCS interrupt controller to work in their own IRQ range this patch moves the INTCS vector away. Needed for future IRQ domain support. Signed-off-by: Magnus Damm Signed-off-by: Rafael J. Wysocki --- arch/arm/mach-shmobile/intc-sh7372.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-shmobile/intc-sh7372.c b/arch/arm/mach-shmobile/intc-sh7372.c index 0ba8b7b69f60..2587a22842f2 100644 --- a/arch/arm/mach-shmobile/intc-sh7372.c +++ b/arch/arm/mach-shmobile/intc-sh7372.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -315,8 +316,6 @@ enum { UNUSED_INTCS = 0, ENABLED_INTCS, - INTCS, - /* interrupt sources INTCS */ /* IRQ0S - IRQ31S */ @@ -430,8 +429,6 @@ static struct intc_vect intcs_vectors[] = { INTCS_VECT(CPORTS2R, 0x1a20), /* CEC */ INTCS_VECT(JPU6E, 0x1a80), - - INTC_VECT(INTCS, 0xf80), }; static struct intc_group intcs_groups[] __initdata = { @@ -494,9 +491,6 @@ static struct intc_mask_reg intcs_mask_registers[] = { { 0xffd5019c, 0xffd501dc, 8, /* IMR7SA3 / IMCR7SA3 */ { MFIS2_INTCS, CPORTS2R, 0, 0, JPU6E, 0, 0, 0 } }, - { 0xffd20104, 0, 16, /* INTAMASK */ - { 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, INTCS } }, }; /* Priority is needed for INTCA to receive the INTCS interrupt */ @@ -561,6 +555,7 @@ static void __iomem *intcs_ffd5; void __init sh7372_init_irq(void) { void __iomem *intevtsa; + int n; intcs_ffd2 = ioremap_nocache(0xffd20000, PAGE_SIZE); intevtsa = intcs_ffd2 + 0x100; @@ -571,9 +566,19 @@ void __init sh7372_init_irq(void) register_intc_controller(&intca_irq_pins_hi_desc); register_intc_controller(&intcs_desc); + /* setup dummy cascade chip for INTCS */ + n = evt2irq(0xf80); + irq_alloc_desc_at(n, numa_node_id()); + irq_set_chip_and_handler_name(n, &dummy_irq_chip, + handle_level_irq, "level"); + set_irq_flags(n, IRQF_VALID); /* yuck */ + /* demux using INTEVTSA */ - irq_set_handler_data(evt2irq(0xf80), (void *)intevtsa); - irq_set_chained_handler(evt2irq(0xf80), intcs_demux); + irq_set_handler_data(n, (void *)intevtsa); + irq_set_chained_handler(n, intcs_demux); + + /* unmask INTCS in INTAMASK */ + iowrite16(0, intcs_ffd2 + 0x104); } static unsigned short ffd2[0x200]; -- cgit v1.2.3 From 3b7b70552afe351a8bd8fff1eb2d60aab2206576 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Wed, 28 Mar 2012 15:53:40 +0900 Subject: ARM: mach-shmobile: sh7372 generic board support via DT V2 Add generic DT board support for the sh7372 SoC V2. SCIF serial ports and timers are kept as regular platform devices. Other on-chip and on-board devices should be configured via the device tree. Tested on the mackerel board via kexec using a zImage kernel with an appended dtb. At this point there is no interrupt controller support in place but such code will be added over time when proper IRQ domain support has been added to INTC. Signed-off-by: Magnus Damm Signed-off-by: Rafael J. Wysocki --- arch/arm/boot/dts/sh7372.dtsi | 21 +++++++++++++++ arch/arm/mach-shmobile/setup-sh7372.c | 48 +++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 arch/arm/boot/dts/sh7372.dtsi (limited to 'arch') diff --git a/arch/arm/boot/dts/sh7372.dtsi b/arch/arm/boot/dts/sh7372.dtsi new file mode 100644 index 000000000000..677fc603f8b3 --- /dev/null +++ b/arch/arm/boot/dts/sh7372.dtsi @@ -0,0 +1,21 @@ +/* + * Device Tree Source for the sh7372 SoC + * + * Copyright (C) 2012 Renesas Solutions Corp. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +/include/ "skeleton.dtsi" + +/ { + compatible = "renesas,sh7372"; + + cpus { + cpu@0 { + compatible = "arm,cortex-a8"; + }; + }; +}; diff --git a/arch/arm/mach-shmobile/setup-sh7372.c b/arch/arm/mach-shmobile/setup-sh7372.c index 2fe8f83ca124..4c7fece5ef92 100644 --- a/arch/arm/mach-shmobile/setup-sh7372.c +++ b/arch/arm/mach-shmobile/setup-sh7372.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1092,3 +1093,50 @@ void __init sh7372_add_early_devices(void) /* override timer setup with soc-specific code */ shmobile_timer.init = sh7372_earlytimer_init; } + +#ifdef CONFIG_USE_OF + +void __init sh7372_add_early_devices_dt(void) +{ + shmobile_setup_delay(800, 1, 3); /* Cortex-A8 @ 800MHz */ + + early_platform_add_devices(sh7372_early_devices, + ARRAY_SIZE(sh7372_early_devices)); + + /* setup early console here as well */ + shmobile_setup_console(); +} + +static const struct of_dev_auxdata sh7372_auxdata_lookup[] __initconst = { + { } +}; + +void __init sh7372_add_standard_devices_dt(void) +{ + /* clocks are setup late during boot in the case of DT */ + sh7372_clock_init(); + + platform_add_devices(sh7372_early_devices, + ARRAY_SIZE(sh7372_early_devices)); + + of_platform_populate(NULL, of_default_bus_match_table, + sh7372_auxdata_lookup, NULL); +} + +static const char *sh7372_boards_compat_dt[] __initdata = { + "renesas,sh7372", + NULL, +}; + +DT_MACHINE_START(SH7372_DT, "Generic SH7372 (Flattened Device Tree)") + .map_io = sh7372_map_io, + .init_early = sh7372_add_early_devices_dt, + .nr_irqs = NR_IRQS_LEGACY, + .init_irq = sh7372_init_irq, + .handle_irq = shmobile_handle_irq_intc, + .init_machine = sh7372_add_standard_devices_dt, + .timer = &shmobile_timer, + .dt_compat = sh7372_boards_compat_dt, +MACHINE_END + +#endif /* CONFIG_USE_OF */ -- cgit v1.2.3 From 011b2e84582ba873f63f2c42829f1677782b5cfe Mon Sep 17 00:00:00 2001 From: Ryan Mallon Date: Thu, 12 Apr 2012 09:11:55 +1000 Subject: Fix build breakage in ep93xx-core Commit ede55aaa8bb2dd37e302b2f94312306d216e5f97 "arm: ep93xx: use DEFINE_RES_* macros" introduced a build breakage in ep93xx due to a typo. Fix it. Signed-off-by: Ryan Mallon Acked-by: Hartley Sweeten --- arch/arm/mach-ep93xx/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index a2cb176cee33..e4a64d2f8955 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -357,7 +357,7 @@ static struct ep93xx_eth_data ep93xx_eth_data; static struct resource ep93xx_eth_resource[] = { DEFINE_RES_MEM(EP93XX_ETHERNET_PHYS_BASE, 0x10000), - DEFINE_REQ_IRQ(IRQ_EP93XX_ETHERNET), + DEFINE_RES_IRQ(IRQ_EP93XX_ETHERNET), }; static u64 ep93xx_eth_dma_mask = DMA_BIT_MASK(32); -- cgit v1.2.3 From a1eacd79a602707f97201edbac9a03edaaea1847 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Wed, 11 Apr 2012 15:44:42 -0700 Subject: arm: ep93xx: use gpio_led_register_device Use gpio_led_register_device to register the two leds connected to the ep93xx. Since this is a common feature for all ep93xx based boards, create a new Kconfig option, EP93XX_SOC_COMMON, to select the LEDS_GPIO_REGISTER feature. The struct gpio_led and struct gpio_led_platform_data variables have been changed to const struct because of: commit 9517f925f2eb9ffca78b3f0f9389fc675bcb572c leds: make *struct gpio_led_platform_data.leds const They have also been marked as __initconst because the helper function gpio_led_register_device makes a deep copy of the platform data to allow it to live in init memory: commit 4440673a95e63ad888a41db596edaa0c55d3a332 leds: provide helper to register "leds-gpio" devices Signed-off-by: Hartley Sweeten Signed-off-by: Ryan Mallon --- arch/arm/mach-ep93xx/Kconfig | 5 +++++ arch/arm/mach-ep93xx/core.c | 16 ++++------------ 2 files changed, 9 insertions(+), 12 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-ep93xx/Kconfig b/arch/arm/mach-ep93xx/Kconfig index 97a249395b5a..fe3c1fa5462b 100644 --- a/arch/arm/mach-ep93xx/Kconfig +++ b/arch/arm/mach-ep93xx/Kconfig @@ -2,6 +2,11 @@ if ARCH_EP93XX menu "Cirrus EP93xx Implementation Options" +config EP93XX_SOC_COMMON + bool + default y + select LEDS_GPIO_REGISTER + config CRUNCH bool "Support for MaverickCrunch" help diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c index e4a64d2f8955..66b1494f23a6 100644 --- a/arch/arm/mach-ep93xx/core.c +++ b/arch/arm/mach-ep93xx/core.c @@ -482,7 +482,7 @@ void __init ep93xx_register_spi(struct ep93xx_spi_info *info, /************************************************************************* * EP93xx LEDs *************************************************************************/ -static struct gpio_led ep93xx_led_pins[] = { +static const struct gpio_led ep93xx_led_pins[] __initconst = { { .name = "platform:grled", .gpio = EP93XX_GPIO_LINE_GRLED, @@ -492,20 +492,11 @@ static struct gpio_led ep93xx_led_pins[] = { }, }; -static struct gpio_led_platform_data ep93xx_led_data = { +static const struct gpio_led_platform_data ep93xx_led_data __initconst = { .num_leds = ARRAY_SIZE(ep93xx_led_pins), .leds = ep93xx_led_pins, }; -static struct platform_device ep93xx_leds = { - .name = "leds-gpio", - .id = -1, - .dev = { - .platform_data = &ep93xx_led_data, - }, -}; - - /************************************************************************* * EP93xx pwm peripheral handling *************************************************************************/ @@ -827,8 +818,9 @@ void __init ep93xx_init_devices(void) platform_device_register(&ep93xx_rtc_device); platform_device_register(&ep93xx_ohci_device); - platform_device_register(&ep93xx_leds); platform_device_register(&ep93xx_wdt_device); + + gpio_led_register_device(-1, &ep93xx_led_data); } void ep93xx_restart(char mode, const char *cmd) -- cgit v1.2.3 From 9de6fe91afcdc38efe398a9d42014a7c920a64db Mon Sep 17 00:00:00 2001 From: Scott Wood Date: Wed, 11 Apr 2012 15:27:52 -0500 Subject: KVM: PPC: add CPU_FTR_EMB_HV to CPU table e6500 support (commit 10241842fbe900276634fee8d37ec48a7d8a762f, "powerpc: Add initial e6500 cpu support" and the introduction of CPU_FTR_EMB_HV (commit 73196cd364a2d972d73fa08da9d81ca3215bed68, "KVM: PPC: e500mc support") collided during merge, leaving e6500's CPU table entry missing CPU_FTR_EMB_HV. Signed-off-by: Scott Wood Signed-off-by: Marcelo Tosatti --- arch/powerpc/include/asm/cputable.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch') diff --git a/arch/powerpc/include/asm/cputable.h b/arch/powerpc/include/asm/cputable.h index 67c34afc6b5d..50d82c8a037f 100644 --- a/arch/powerpc/include/asm/cputable.h +++ b/arch/powerpc/include/asm/cputable.h @@ -395,7 +395,7 @@ extern const char *powerpc_base_platform; #define CPU_FTRS_E6500 (CPU_FTR_USE_TB | CPU_FTR_NODSISRALIGN | \ CPU_FTR_L2CSR | CPU_FTR_LWSYNC | CPU_FTR_NOEXECUTE | \ CPU_FTR_DBELL | CPU_FTR_POPCNTB | CPU_FTR_POPCNTD | \ - CPU_FTR_DEBUG_LVL_EXC) + CPU_FTR_DEBUG_LVL_EXC | CPU_FTR_EMB_HV) #define CPU_FTRS_GENERIC_32 (CPU_FTR_COMMON | CPU_FTR_NODSISRALIGN) /* 64-bit CPUs */ -- cgit v1.2.3 From 4692d77fc3c8978a36406a3cf9e8b899f86f68f1 Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Wed, 4 Apr 2012 19:39:58 +0200 Subject: x86-32: Introduce CONFIG_X86_DEV_DMA_OPS 32-bit x86 systems may need their own DMA operations, so add a new config option, which is turned on for 64-bit systems. This patch has no functional effect but it paves the way for supporting the STA2x11 I/O Hub and possibly other chips. Signed-off-by: Alessandro Rubini Link: http://lkml.kernel.org/r/f79fcc1a2e17ef942e1b798b92aac43a80202532.1333560789.git.rubini@gnudd.com Acked-by: Giancarlo Asnaghi Cc: Alan Cox Signed-off-by: H. Peter Anvin --- arch/x86/Kconfig | 5 +++++ arch/x86/include/asm/device.h | 4 ++-- arch/x86/include/asm/dma-mapping.h | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) (limited to 'arch') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 1d14cc6b79ad..07b412aed38b 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -12,6 +12,7 @@ config X86_32 config X86_64 def_bool 64BIT + select X86_DEV_DMA_OPS ### Arch settings config X86 @@ -2215,6 +2216,10 @@ config HAVE_TEXT_POKE_SMP bool select STOP_MACHINE if SMP +config X86_DEV_DMA_OPS + bool + depends on X86_64 + source "net/Kconfig" source "drivers/Kconfig" diff --git a/arch/x86/include/asm/device.h b/arch/x86/include/asm/device.h index 63a2a03d7d51..93e1c55f14ab 100644 --- a/arch/x86/include/asm/device.h +++ b/arch/x86/include/asm/device.h @@ -5,8 +5,8 @@ struct dev_archdata { #ifdef CONFIG_ACPI void *acpi_handle; #endif -#ifdef CONFIG_X86_64 -struct dma_map_ops *dma_ops; +#ifdef CONFIG_X86_DEV_DMA_OPS + struct dma_map_ops *dma_ops; #endif #if defined(CONFIG_INTEL_IOMMU) || defined(CONFIG_AMD_IOMMU) void *iommu; /* hook for IOMMU specific extension */ diff --git a/arch/x86/include/asm/dma-mapping.h b/arch/x86/include/asm/dma-mapping.h index 4b4331d71935..09aa473e2917 100644 --- a/arch/x86/include/asm/dma-mapping.h +++ b/arch/x86/include/asm/dma-mapping.h @@ -30,7 +30,7 @@ extern struct dma_map_ops *dma_ops; static inline struct dma_map_ops *get_dma_ops(struct device *dev) { -#ifdef CONFIG_X86_32 +#ifndef CONFIG_X86_DEV_DMA_OPS return dma_ops; #else if (unlikely(!dev) || !dev->archdata.dma_ops) -- cgit v1.2.3 From f7219a5300ba753b0c762d631763bd878b8bb00c Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Wed, 4 Apr 2012 19:40:10 +0200 Subject: x86: Introduce CONFIG_X86_DMA_REMAP The default functions phys_to_dma, dma_to_phys implement identity mapping as fast inline functions. Some systems, however, may need a custom function to implement its own mapping between CPU addresses and device addresses. This new configuration option allows the functions to be external when needed (such as for the ConneXt device) Signed-off-by: Alessandro Rubini Link: http://lkml.kernel.org/r/6e4329b772df675f1c442f68e59e844e4dd8c965.1333560789.git.rubini@gnudd.com Acked-by: Giancarlo Asnaghi Cc: Alan Cox Signed-off-by: H. Peter Anvin --- arch/x86/Kconfig | 3 +++ arch/x86/include/asm/dma-mapping.h | 7 +++++++ 2 files changed, 10 insertions(+) (limited to 'arch') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 07b412aed38b..95ca56036030 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2220,6 +2220,9 @@ config X86_DEV_DMA_OPS bool depends on X86_64 +config X86_DMA_REMAP + bool + source "net/Kconfig" source "drivers/Kconfig" diff --git a/arch/x86/include/asm/dma-mapping.h b/arch/x86/include/asm/dma-mapping.h index 09aa473e2917..61c0bd25845a 100644 --- a/arch/x86/include/asm/dma-mapping.h +++ b/arch/x86/include/asm/dma-mapping.h @@ -62,6 +62,12 @@ extern void *dma_generic_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_addr, gfp_t flag, struct dma_attrs *attrs); +#ifdef CONFIG_X86_DMA_REMAP /* Platform code defines bridge-specific code */ +extern bool dma_capable(struct device *dev, dma_addr_t addr, size_t size); +extern dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr); +extern phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr); +#else + static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) { if (!dev->dma_mask) @@ -79,6 +85,7 @@ static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr) { return daddr; } +#endif /* CONFIG_X86_DMA_REMAP */ static inline void dma_cache_sync(struct device *dev, void *vaddr, size_t size, -- cgit v1.2.3 From 83125a3a189ec34fb22a04e8efad69ae6d52674a Mon Sep 17 00:00:00 2001 From: Alessandro Rubini Date: Wed, 4 Apr 2012 19:40:21 +0200 Subject: x86, platform: Initial support for sta2x11 I/O hub The "ConneXt" sta2x11 I/O Hub is a bridge from PCIe to AMBA, and is used as main chipset in some Atom boards. The set of peripherals it exports live in an AMBA bus internal to the chip, so a custom remapping of addresses is needed. This is implemented by fixup calls for the PCI deivices, based on CONFIG_X86_DEV_DMA_OPS and CONFIG_X86_DMA_REMAP . Signed-off-by: Alessandro Rubini Link: http://lkml.kernel.org/r/ddca670ca8180e52d49b3fe642742ddd23ab2cb2.1333560789.git.rubini@gnudd.com Acked-by: Giancarlo Asnaghi Cc: Alan Cox Signed-off-by: H. Peter Anvin --- arch/x86/Kconfig | 28 +++- arch/x86/pci/Makefile | 2 + arch/x86/pci/sta2x11-fixup.c | 366 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 391 insertions(+), 5 deletions(-) create mode 100644 arch/x86/pci/sta2x11-fixup.c (limited to 'arch') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 95ca56036030..f9ed801abaf9 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -329,6 +329,7 @@ config X86_EXTENDED_PLATFORM NUMAQ (IBM/Sequent) RDC R-321x SoC SGI 320/540 (Visual Workstation) + STA2X11-based (e.g. Northville) Summit/EXA (IBM x440) Unisys ES7000 IA32 series Moorestown MID devices @@ -461,10 +462,10 @@ config X86_32_NON_STANDARD depends on X86_32 && SMP depends on X86_EXTENDED_PLATFORM ---help--- - This option compiles in the NUMAQ, Summit, bigsmp, ES7000, default - subarchitectures. It is intended for a generic binary kernel. - if you select them all, kernel will probe it one by one. and will - fallback to default. + This option compiles in the NUMAQ, Summit, bigsmp, ES7000, + STA2X11, default subarchitectures. It is intended for a generic + binary kernel. If you select them all, kernel will probe it + one by one and will fallback to default. # Alphabetically sorted list of Non standard 32 bit platforms @@ -504,6 +505,22 @@ config X86_VISWS A kernel compiled for the Visual Workstation will run on general PCs as well. See for details. +config STA2X11 + bool "STA2X11 Companion Chip Support" + depends on X86_32_NON_STANDARD && PCI + select X86_DEV_DMA_OPS + select X86_DMA_REMAP + select SWIOTLB + select MFD_STA2X11 + select ARCH_REQUIRE_GPIOLIB + default n + ---help--- + This adds support for boards based on the STA2X11 IO-Hub, + a.k.a. "ConneXt". The chip is used in place of the standard + PC chipset, so all "standard" peripherals are missing. If this + option is selected the kernel will still be able to boot on + standard PC machines. + config X86_SUMMIT bool "Summit/EXA (IBM x440)" depends on X86_32_NON_STANDARD @@ -2218,10 +2235,11 @@ config HAVE_TEXT_POKE_SMP config X86_DEV_DMA_OPS bool - depends on X86_64 + depends on X86_64 || STA2X11 config X86_DMA_REMAP bool + depends on STA2X11 source "net/Kconfig" diff --git a/arch/x86/pci/Makefile b/arch/x86/pci/Makefile index e76e18c94a3c..3af5a1e79c9c 100644 --- a/arch/x86/pci/Makefile +++ b/arch/x86/pci/Makefile @@ -11,6 +11,8 @@ obj-$(CONFIG_X86_INTEL_CE) += ce4100.o obj-$(CONFIG_ACPI) += acpi.o obj-y += legacy.o irq.o +obj-$(CONFIG_STA2X11) += sta2x11-fixup.o + obj-$(CONFIG_X86_VISWS) += visws.o obj-$(CONFIG_X86_NUMAQ) += numaq_32.o diff --git a/arch/x86/pci/sta2x11-fixup.c b/arch/x86/pci/sta2x11-fixup.c new file mode 100644 index 000000000000..9d8a509c9730 --- /dev/null +++ b/arch/x86/pci/sta2x11-fixup.c @@ -0,0 +1,366 @@ +/* + * arch/x86/pci/sta2x11-fixup.c + * glue code for lib/swiotlb.c and DMA translation between STA2x11 + * AMBA memory mapping and the X86 memory mapping + * + * ST Microelectronics ConneXt (STA2X11/STA2X10) + * + * Copyright (c) 2010-2011 Wind River Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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 +#include +#include +#include + +#define STA2X11_SWIOTLB_SIZE (4*1024*1024) +extern int swiotlb_late_init_with_default_size(size_t default_size); + +/* + * We build a list of bus numbers that are under the ConneXt. The + * main bridge hosts 4 busses, which are the 4 endpoints, in order. + */ +#define STA2X11_NR_EP 4 /* 0..3 included */ +#define STA2X11_NR_FUNCS 8 /* 0..7 included */ +#define STA2X11_AMBA_SIZE (512 << 20) + +struct sta2x11_ahb_regs { /* saved during suspend */ + u32 base, pexlbase, pexhbase, crw; +}; + +struct sta2x11_mapping { + u32 amba_base; + int is_suspended; + struct sta2x11_ahb_regs regs[STA2X11_NR_FUNCS]; +}; + +struct sta2x11_instance { + struct list_head list; + int bus0; + struct sta2x11_mapping map[STA2X11_NR_EP]; +}; + +static LIST_HEAD(sta2x11_instance_list); + +/* At probe time, record new instances of this bridge (likely one only) */ +static void sta2x11_new_instance(struct pci_dev *pdev) +{ + struct sta2x11_instance *instance; + + instance = kzalloc(sizeof(*instance), GFP_ATOMIC); + if (!instance) + return; + /* This has a subordinate bridge, with 4 more-subordinate ones */ + instance->bus0 = pdev->subordinate->number + 1; + + if (list_empty(&sta2x11_instance_list)) { + int size = STA2X11_SWIOTLB_SIZE; + /* First instance: register your own swiotlb area */ + dev_info(&pdev->dev, "Using SWIOTLB (size %i)\n", size); + if (swiotlb_late_init_with_default_size(size)) + dev_emerg(&pdev->dev, "init swiotlb failed\n"); + } + list_add(&instance->list, &sta2x11_instance_list); +} +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, 0xcc17, sta2x11_new_instance); + +/* + * Utility functions used in this file from below + */ +static struct sta2x11_instance *sta2x11_pdev_to_instance(struct pci_dev *pdev) +{ + struct sta2x11_instance *instance; + int ep; + + list_for_each_entry(instance, &sta2x11_instance_list, list) { + ep = pdev->bus->number - instance->bus0; + if (ep >= 0 && ep < STA2X11_NR_EP) + return instance; + } + return NULL; +} + +static int sta2x11_pdev_to_ep(struct pci_dev *pdev) +{ + struct sta2x11_instance *instance; + + instance = sta2x11_pdev_to_instance(pdev); + if (!instance) + return -1; + + return pdev->bus->number - instance->bus0; +} + +static struct sta2x11_mapping *sta2x11_pdev_to_mapping(struct pci_dev *pdev) +{ + struct sta2x11_instance *instance; + int ep; + + instance = sta2x11_pdev_to_instance(pdev); + if (!instance) + return NULL; + ep = sta2x11_pdev_to_ep(pdev); + return instance->map + ep; +} + +/* This is exported, as some devices need to access the MFD registers */ +struct sta2x11_instance *sta2x11_get_instance(struct pci_dev *pdev) +{ + return sta2x11_pdev_to_instance(pdev); +} +EXPORT_SYMBOL(sta2x11_get_instance); + + +/** + * p2a - Translate physical address to STA2x11 AMBA address, + * used for DMA transfers to STA2x11 + * @p: Physical address + * @pdev: PCI device (must be hosted within the connext) + */ +static dma_addr_t p2a(dma_addr_t p, struct pci_dev *pdev) +{ + struct sta2x11_mapping *map; + dma_addr_t a; + + map = sta2x11_pdev_to_mapping(pdev); + a = p + map->amba_base; + return a; +} + +/** + * a2p - Translate STA2x11 AMBA address to physical address + * used for DMA transfers from STA2x11 + * @a: STA2x11 AMBA address + * @pdev: PCI device (must be hosted within the connext) + */ +static dma_addr_t a2p(dma_addr_t a, struct pci_dev *pdev) +{ + struct sta2x11_mapping *map; + dma_addr_t p; + + map = sta2x11_pdev_to_mapping(pdev); + p = a - map->amba_base; + return p; +} + +/** + * sta2x11_swiotlb_alloc_coherent - Allocate swiotlb bounce buffers + * returns virtual address. This is the only "special" function here. + * @dev: PCI device + * @size: Size of the buffer + * @dma_handle: DMA address + * @flags: memory flags + */ +static void *sta2x11_swiotlb_alloc_coherent(struct device *dev, + size_t size, + dma_addr_t *dma_handle, + gfp_t flags, + struct dma_attrs *attrs) +{ + void *vaddr; + + vaddr = dma_generic_alloc_coherent(dev, size, dma_handle, flags, attrs); + if (!vaddr) + vaddr = swiotlb_alloc_coherent(dev, size, dma_handle, flags); + *dma_handle = p2a(*dma_handle, to_pci_dev(dev)); + return vaddr; +} + +/* We have our own dma_ops: the same as swiotlb but from alloc (above) */ +static struct dma_map_ops sta2x11_dma_ops = { + .alloc = sta2x11_swiotlb_alloc_coherent, + .free = swiotlb_free_coherent, + .map_page = swiotlb_map_page, + .unmap_page = swiotlb_unmap_page, + .map_sg = swiotlb_map_sg_attrs, + .unmap_sg = swiotlb_unmap_sg_attrs, + .sync_single_for_cpu = swiotlb_sync_single_for_cpu, + .sync_single_for_device = swiotlb_sync_single_for_device, + .sync_sg_for_cpu = swiotlb_sync_sg_for_cpu, + .sync_sg_for_device = swiotlb_sync_sg_for_device, + .mapping_error = swiotlb_dma_mapping_error, + .dma_supported = NULL, /* FIXME: we should use this instead! */ +}; + +/* At setup time, we use our own ops if the device is a ConneXt one */ +static void sta2x11_setup_pdev(struct pci_dev *pdev) +{ + struct sta2x11_instance *instance = sta2x11_pdev_to_instance(pdev); + + if (!instance) /* either a sta2x11 bridge or another ST device */ + return; + pci_set_consistent_dma_mask(pdev, STA2X11_AMBA_SIZE - 1); + pci_set_dma_mask(pdev, STA2X11_AMBA_SIZE - 1); + pdev->dev.archdata.dma_ops = &sta2x11_dma_ops; + + /* We must enable all devices as master, for audio DMA to work */ + pci_set_master(pdev); +} +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, sta2x11_setup_pdev); + +/* + * The following three functions are exported (used in swiotlb: FIXME) + */ +/** + * dma_capable - Check if device can manage DMA transfers (FIXME: kill it) + * @dev: device for a PCI device + * @addr: DMA address + * @size: DMA size + */ +bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) +{ + struct sta2x11_mapping *map; + + if (dev->archdata.dma_ops != &sta2x11_dma_ops) { + if (!dev->dma_mask) + return false; + return addr + size - 1 <= *dev->dma_mask; + } + + map = sta2x11_pdev_to_mapping(to_pci_dev(dev)); + + if (!map || (addr < map->amba_base)) + return false; + if (addr + size >= map->amba_base + STA2X11_AMBA_SIZE) { + return false; + } + + return true; +} + +/** + * phys_to_dma - Return the DMA AMBA address used for this STA2x11 device + * @dev: device for a PCI device + * @paddr: Physical address + */ +dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr) +{ + if (dev->archdata.dma_ops != &sta2x11_dma_ops) + return paddr; + return p2a(paddr, to_pci_dev(dev)); +} + +/** + * dma_to_phys - Return the physical address used for this STA2x11 DMA address + * @dev: device for a PCI device + * @daddr: STA2x11 AMBA DMA address + */ +phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr) +{ + if (dev->archdata.dma_ops != &sta2x11_dma_ops) + return daddr; + return a2p(daddr, to_pci_dev(dev)); +} + + +/* + * At boot we must set up the mappings for the pcie-to-amba bridge. + * It involves device access, and the same happens at suspend/resume time + */ + +#define AHB_MAPB 0xCA4 +#define AHB_CRW(i) (AHB_MAPB + 0 + (i) * 0x10) +#define AHB_CRW_SZMASK 0xfffffc00UL +#define AHB_CRW_ENABLE (1 << 0) +#define AHB_CRW_WTYPE_MEM (2 << 1) +#define AHB_CRW_ROE (1UL << 3) /* Relax Order Ena */ +#define AHB_CRW_NSE (1UL << 4) /* No Snoop Enable */ +#define AHB_BASE(i) (AHB_MAPB + 4 + (i) * 0x10) +#define AHB_PEXLBASE(i) (AHB_MAPB + 8 + (i) * 0x10) +#define AHB_PEXHBASE(i) (AHB_MAPB + 12 + (i) * 0x10) + +/* At probe time, enable mapping for each endpoint, using the pdev */ +static void sta2x11_map_ep(struct pci_dev *pdev) +{ + struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev); + int i; + + if (!map) + return; + pci_read_config_dword(pdev, AHB_BASE(0), &map->amba_base); + + /* Configure AHB mapping */ + pci_write_config_dword(pdev, AHB_PEXLBASE(0), 0); + pci_write_config_dword(pdev, AHB_PEXHBASE(0), 0); + pci_write_config_dword(pdev, AHB_CRW(0), STA2X11_AMBA_SIZE | + AHB_CRW_WTYPE_MEM | AHB_CRW_ENABLE); + + /* Disable all the other windows */ + for (i = 1; i < STA2X11_NR_FUNCS; i++) + pci_write_config_dword(pdev, AHB_CRW(i), 0); + + dev_info(&pdev->dev, + "sta2x11: Map EP %i: AMBA address %#8x-%#8x\n", + sta2x11_pdev_to_ep(pdev), map->amba_base, + map->amba_base + STA2X11_AMBA_SIZE - 1); +} +DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, sta2x11_map_ep); + +#ifdef CONFIG_PM /* Some register values must be saved and restored */ + +static void suspend_mapping(struct pci_dev *pdev) +{ + struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev); + int i; + + if (!map) + return; + + if (map->is_suspended) + return; + map->is_suspended = 1; + + /* Save all window configs */ + for (i = 0; i < STA2X11_NR_FUNCS; i++) { + struct sta2x11_ahb_regs *regs = map->regs + i; + + pci_read_config_dword(pdev, AHB_BASE(i), ®s->base); + pci_read_config_dword(pdev, AHB_PEXLBASE(i), ®s->pexlbase); + pci_read_config_dword(pdev, AHB_PEXHBASE(i), ®s->pexhbase); + pci_read_config_dword(pdev, AHB_CRW(i), ®s->crw); + } +} +DECLARE_PCI_FIXUP_SUSPEND(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, suspend_mapping); + +static void resume_mapping(struct pci_dev *pdev) +{ + struct sta2x11_mapping *map = sta2x11_pdev_to_mapping(pdev); + int i; + + if (!map) + return; + + + if (!map->is_suspended) + goto out; + map->is_suspended = 0; + + /* Restore all window configs */ + for (i = 0; i < STA2X11_NR_FUNCS; i++) { + struct sta2x11_ahb_regs *regs = map->regs + i; + + pci_write_config_dword(pdev, AHB_BASE(i), regs->base); + pci_write_config_dword(pdev, AHB_PEXLBASE(i), regs->pexlbase); + pci_write_config_dword(pdev, AHB_PEXHBASE(i), regs->pexhbase); + pci_write_config_dword(pdev, AHB_CRW(i), regs->crw); + } +out: + pci_set_master(pdev); /* Like at boot, enable master on all devices */ +} +DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_STMICRO, PCI_ANY_ID, resume_mapping); + +#endif /* CONFIG_PM */ -- cgit v1.2.3 From af8a2fe12fae1b59178dc96e396e5665bcbea7da Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Sun, 8 Apr 2012 21:18:28 -0700 Subject: ASoC: sh: fsi: use simple-card instead of fsi-ak4642 This patch uses simple-card driver instead of fsi-ak4642 on each board. To select AK4642 driver, each boards select it on Kconfig. This patch removes fsi-ak4642 driver which is no longer needed Signed-off-by: Kuninori Morimoto Signed-off-by: Mark Brown --- arch/arm/mach-shmobile/Kconfig | 2 + arch/arm/mach-shmobile/board-ap4evb.c | 15 ++++- arch/arm/mach-shmobile/board-mackerel.c | 15 ++++- arch/sh/boards/Kconfig | 1 + arch/sh/boards/mach-se/7724/setup.c | 15 ++++- include/sound/sh_fsi.h | 12 ---- sound/soc/sh/Kconfig | 8 --- sound/soc/sh/Makefile | 2 - sound/soc/sh/fsi-ak4642.c | 108 -------------------------------- 9 files changed, 39 insertions(+), 139 deletions(-) delete mode 100644 sound/soc/sh/fsi-ak4642.c (limited to 'arch') diff --git a/arch/arm/mach-shmobile/Kconfig b/arch/arm/mach-shmobile/Kconfig index 34560cab45d9..2cda0c2af230 100644 --- a/arch/arm/mach-shmobile/Kconfig +++ b/arch/arm/mach-shmobile/Kconfig @@ -58,6 +58,7 @@ config MACH_AP4EVB depends on ARCH_SH7372 select ARCH_REQUIRE_GPIOLIB select SH_LCD_MIPI_DSI + select SND_SOC_AK4642 if SND_SIMPLE_CARD choice prompt "AP4EVB LCD panel selection" @@ -82,6 +83,7 @@ config MACH_MACKEREL bool "mackerel board" depends on ARCH_SH7372 select ARCH_REQUIRE_GPIOLIB + select SND_SOC_AK4642 if SND_SIMPLE_CARD config MACH_KOTA2 bool "KOTA2 board" diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c index b56dde2732bb..b39751244daa 100644 --- a/arch/arm/mach-shmobile/board-ap4evb.c +++ b/arch/arm/mach-shmobile/board-ap4evb.c @@ -50,6 +50,7 @@ #include #include +#include #include