From eebd0bcbf43654eccd94eb7cbfc7c13530cd70aa Mon Sep 17 00:00:00 2001 From: Ben Shelton Date: Tue, 1 Apr 2014 11:31:31 -0500 Subject: [PATCH 01/84] shared: Adding mcopy syscall for x64 Signed-off-by: Ben Shelton Acked-by: Scot Salmon Acked-by: Terry Wilcox Natinst-ReviewBoard-ID: 69848 [gratian: convert to new syscall table format for arm] Signed-off-by: Gratian Crisan [bstreiff: update the number for this painful out-of-tree syscall] Signed-off-by: Brandon Streiff [gratian: update due to new syscall introduced by ecb8ac8b1f14 ("mm/madvise: introduce process_madvise() syscall: an external memory hinting AP")] Signed-off-by: Gratian Crisan [gratian: update syscall numbers to account for upstream additions; dropped arm bits] Signed-off-by: Gratian Crisan [gratian: bump syscall number to account for upstream process_mrelease addition] Signed-off-by: Gratian Crisan --- arch/x86/entry/syscalls/syscall_64.tbl | 1 + include/linux/syscalls.h | 3 +++ include/uapi/asm-generic/unistd.h | 5 +++- kernel/ksysfs.c | 14 +++++++++++ mm/maccess.c | 32 ++++++++++++++++++++++++++ 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl index 18b5500ea8bfd..4498f51c528dc 100644 --- a/arch/x86/entry/syscalls/syscall_64.tbl +++ b/arch/x86/entry/syscalls/syscall_64.tbl @@ -370,6 +370,7 @@ 446 common landlock_restrict_self sys_landlock_restrict_self 447 common memfd_secret sys_memfd_secret 448 common process_mrelease sys_process_mrelease +449 common mcopy sys_mcopy # # Due to a historical design error, certain syscalls are numbered differently diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 252243c7783db..1a4223a420e5b 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -900,6 +900,9 @@ asmlinkage long sys_execve(const char __user *filename, /* mm/fadvise.c */ asmlinkage long sys_fadvise64_64(int fd, loff_t offset, loff_t len, int advice); +/* mm/maccess.c */ +asmlinkage long sys_mcopy(void * __user dest, void * __user src, size_t len); + /* mm/, CONFIG_MMU only */ asmlinkage long sys_swapon(const char __user *specialfile, int swap_flags); asmlinkage long sys_swapoff(const char __user *specialfile); diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h index 1c5fb86d455ab..dd0f6c5414759 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -880,8 +880,11 @@ __SYSCALL(__NR_memfd_secret, sys_memfd_secret) #define __NR_process_mrelease 448 __SYSCALL(__NR_process_mrelease, sys_process_mrelease) +#define __NR_mcopy 449 +__SYSCALL(__NR_mcopy, sys_mcopy) + #undef __NR_syscalls -#define __NR_syscalls 449 +#define __NR_syscalls 450 /* * 32 bit systems traditionally used different diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c index dfff31ed644a6..e3f0d1f9054aa 100644 --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c @@ -17,6 +17,7 @@ #include #include #include +#include #include /* rcu_expedited and rcu_normal */ @@ -155,6 +156,16 @@ static ssize_t fscaps_show(struct kobject *kobj, } KERNEL_ATTR_RO(fscaps); +#ifdef __NR_mcopy +static ssize_t ni_syscall_mcopy_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", __NR_mcopy); +} +KERNEL_ATTR_RO(ni_syscall_mcopy); +#endif + #ifndef CONFIG_TINY_RCU int rcu_expedited; static ssize_t rcu_expedited_show(struct kobject *kobj, @@ -240,6 +251,9 @@ static struct attribute * kernel_attrs[] = { #endif #ifdef CONFIG_PREEMPT_RT &realtime_attr.attr, +#endif +#ifdef __NR_mcopy + &ni_syscall_mcopy_attr.attr, #endif NULL }; diff --git a/mm/maccess.c b/mm/maccess.c index d3f1a1f0b1c1a..c56b68440b197 100644 --- a/mm/maccess.c +++ b/mm/maccess.c @@ -5,6 +5,7 @@ #include #include #include +#include bool __weak copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size) @@ -335,3 +336,34 @@ long strnlen_user_nofault(const void __user *unsafe_addr, long count) return ret; } + +/* + * Safely copy 'len' bytes from user space 'src' to user space 'dst'. + * 'len' must be less than or equal to 64. In particular, safely here + * means that if we are trying to copy memory that has been freed and + * unmapped we don't crash. + * + * Returns + * 0 copy completed successfully + * + * EFAULT if either the source or destination blocks are not + * valid + * + * EINVAL len is greater than 64 + * + */ +SYSCALL_DEFINE3(mcopy, void*, dst, void*, src, size_t, len) +{ + char buf[64]; + + if (len > 64) + return -EINVAL; + + if (copy_from_user(buf, src, len)) + return -EFAULT; + + if (copy_to_user(dst, buf, len)) + return -EFAULT; + + return 0; +} From 9754343a7351f4f52fd5de0cfec56684163d48c1 Mon Sep 17 00:00:00 2001 From: Terry Wilcox Date: Mon, 19 May 2014 15:28:03 -0500 Subject: [PATCH 02/84] kernel: Providing API to allow userland programs to request a cold or warm reboot Another group is requesting that we provide an API to allow them to signal that the next reboot should be "cold". They need this to guarantee that the FPGA will not be running and cause the system to reboot at a bad time. This change creates a RW file at /sys/kernel/ni_requested_reboot_type. The default value is 0. - If when we reboot the value is 0 then we do the normal reset behavior. - If the value is 1 we attempt to do a PCI reboot (using the CF9 register) and fall back to the normal reboot method if that fails. - If the value is 2 we attempt to do an ACPI reboot and fall back to the normal reboot method if that fails. We selected a reboot using the CF9 register over attempting to do an EFI reboot because we don't have much time to test this feature and we've found EFI features to be fairly buggy. For next release the plan is to do an EFI cold reboot, but put it in early enough to properly test it. Rebooting using the CF9 register should work on all x64 hardware that we will support for 2014 (smasher and hammerhead). Signed-off-by: Terry Wilcox Acked-by: Brad Mouring Natinst-ReviewBoard-ID: 68018 --- arch/x86/kernel/reboot.c | 49 ++++++++++++++++++++++++++++++++-------- kernel/ksysfs.c | 18 +++++++++++++++ 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/arch/x86/kernel/reboot.c b/arch/x86/kernel/reboot.c index 0a40df66a40de..f7bd92938dea1 100644 --- a/arch/x86/kernel/reboot.c +++ b/arch/x86/kernel/reboot.c @@ -39,6 +39,14 @@ void (*pm_power_off)(void); EXPORT_SYMBOL(pm_power_off); +enum requested_reboot_type { + REQUEST_DEFAULT_REBOOT, + REQUEST_COLD_REBOOT, + REQUEST_WARM_REBOOT +}; + +enum requested_reboot_type requested_reboot_type = REQUEST_DEFAULT_REBOOT; + /* * This is set if we need to go through the 'emergency' path. * When machine_emergency_restart() is called, we may be on @@ -571,6 +579,21 @@ void __attribute__((weak)) mach_reboot_fixups(void) { } +static void native_machine_restart_cf9(void) +{ + if (port_cf9_safe) { + u8 reboot_code = reboot_mode == REBOOT_WARM ? 0x06 : 0x0E; + u8 cf9 = inb(0xcf9) & ~reboot_code; + + outb(cf9|2, 0xcf9); /* Request hard reset */ + udelay(50); + /* Actually do the reset */ + outb(cf9|reboot_code, 0xcf9); + udelay(50); + } +} + + /* * To the best of our knowledge Windows compatible x86 hardware expects * the following on reboot: @@ -602,6 +625,22 @@ static void native_machine_emergency_restart(void) tboot_shutdown(TB_SHUTDOWN_REBOOT); + + switch (requested_reboot_type) { + case REQUEST_COLD_REBOOT: + port_cf9_safe = true; + native_machine_restart_cf9(); + break; + + case REQUEST_WARM_REBOOT: + acpi_reboot(); + break; + + case REQUEST_DEFAULT_REBOOT: + default: + break; + } + /* Tell the BIOS if we want cold or warm reboot */ mode = reboot_mode == REBOOT_WARM ? 0x1234 : 0; *((unsigned short *)__va(0x472)) = mode; @@ -657,15 +696,7 @@ static void native_machine_emergency_restart(void) fallthrough; case BOOT_CF9_SAFE: - if (port_cf9_safe) { - u8 reboot_code = reboot_mode == REBOOT_WARM ? 0x06 : 0x0E; - u8 cf9 = inb(0xcf9) & ~reboot_code; - outb(cf9|2, 0xcf9); /* Request hard reset */ - udelay(50); - /* Actually do the reset */ - outb(cf9|reboot_code, 0xcf9); - udelay(50); - } + native_machine_restart_cf9(); reboot_type = BOOT_TRIPLE; break; diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c index e3f0d1f9054aa..4c779a24eb030 100644 --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c @@ -202,6 +202,23 @@ static ssize_t rcu_normal_store(struct kobject *kobj, KERNEL_ATTR_RW(rcu_normal); #endif /* #ifndef CONFIG_TINY_RCU */ +extern int requested_reboot_type; +static ssize_t ni_requested_reboot_type_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", requested_reboot_type); +} +static ssize_t ni_requested_reboot_type_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + if (kstrtoint(buf, 0, &requested_reboot_type)) + return -EINVAL; + + return count; +} +KERNEL_ATTR_RW(ni_requested_reboot_type); + /* * Make /sys/kernel/notes give the raw contents of our kernel .notes section. */ @@ -255,6 +272,7 @@ static struct attribute * kernel_attrs[] = { #ifdef __NR_mcopy &ni_syscall_mcopy_attr.attr, #endif + &ni_requested_reboot_type_attr.attr, NULL }; From e0d757ebaf3e1f2d24179df76cf308a936989345 Mon Sep 17 00:00:00 2001 From: Ben Shelton Date: Tue, 10 Jun 2014 15:04:11 -0500 Subject: [PATCH 03/84] kernel: Add config option to specify NI cold boot support Currently, we provide NI cold boot support on x64 targets. However, at some future point, we may wish to provide this support on other targets as well. Adding a config option to specify that a target supports NI cold boot functionality; this fixes the build for Zynq targets and doesn't paint us into a corner later. Signed-off-by: Ben Shelton --- arch/x86/Kconfig | 4 ++++ kernel/ksysfs.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 821f3efcc257f..0260daadd5d8e 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -268,6 +268,10 @@ config X86 select X86_FEATURE_NAMES if PROC_FS select PROC_PID_ARCH_STATUS if PROC_FS imply IMA_SECURE_AND_OR_TRUSTED_BOOT if EFI + select NI_COLD_BOOT_SUPPORT + +config NI_COLD_BOOT_SUPPORT + def_bool n config INSTRUCTION_DECODER def_bool y diff --git a/kernel/ksysfs.c b/kernel/ksysfs.c index 4c779a24eb030..49b35cb4c0520 100644 --- a/kernel/ksysfs.c +++ b/kernel/ksysfs.c @@ -202,6 +202,7 @@ static ssize_t rcu_normal_store(struct kobject *kobj, KERNEL_ATTR_RW(rcu_normal); #endif /* #ifndef CONFIG_TINY_RCU */ +#ifdef CONFIG_NI_COLD_BOOT_SUPPORT extern int requested_reboot_type; static ssize_t ni_requested_reboot_type_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -218,6 +219,7 @@ static ssize_t ni_requested_reboot_type_store(struct kobject *kobj, return count; } KERNEL_ATTR_RW(ni_requested_reboot_type); +#endif /* * Make /sys/kernel/notes give the raw contents of our kernel .notes section. @@ -272,7 +274,9 @@ static struct attribute * kernel_attrs[] = { #ifdef __NR_mcopy &ni_syscall_mcopy_attr.attr, #endif +#ifdef CONFIG_NI_COLD_BOOT_SUPPORT &ni_requested_reboot_type_attr.attr, +#endif NULL }; From 472dba030a74a0ca4ffecf27ec55954e01bcfe1d Mon Sep 17 00:00:00 2001 From: Sankara S Muthukrishnan Date: Fri, 5 Oct 2012 13:21:14 -0500 Subject: [PATCH 04/84] irq: Add priority support to /proc/irq/../ This patch allows configuring priority for different irq threads through the /proc/irq/ system (much same as the existing mechanism to configure the core affinity for irqs). Priority of an already running irq thread can be changed also, however, the new priority will take into effect when the irq thread is scheduled to run the next time. An irq thread that gets created will use the priority specified in the /proc/irq/../priority file. Thanks Josh Cartwright for suggesting this idea. Also fix up the original commit by moving the code out from under the CONFIG_SMP conditional to allow building a uniprocessor kernel. Signed-off-by: Sankara S Muthukrishnan Signed-off-by: Josh Cartwright Acked-by: Josh Cartwright Acked-by: Jeff Westfahl Acked-by: Gratian Crisan [gratian: rename setup_affinity() to irq_setup_affinity() per commit 43564bd97d0e ("genirq: Rename setup_affinity() to irq_setup_affinity()")] Signed-off-by: Gratian Crisan [bstreiff: drop dependency on reverted commit 87fdb9bd58c3460fa8c0655b3b82ece1d6bbb955 ("boot: add kernel boot parameters for kernel thread priorities")] [bstreiff: convert from file_operations to proc_ops] Signed-off-by: Brandon Streiff [gratian: convert MAX_USER_RT_PRIO to MAX_RT_PRIO following commit ae18ad281e82 ("sched: Remove MAX_USER_RT_PRIO")] Signed-off-by: Gratian Crisan --- include/linux/interrupt.h | 3 ++ include/linux/irq.h | 1 + kernel/irq/internals.h | 1 + kernel/irq/irqdesc.c | 1 + kernel/irq/manage.c | 62 +++++++++++++++++++++++++++++++++++++++ kernel/irq/proc.c | 56 +++++++++++++++++++++++++++++++++++ 6 files changed, 124 insertions(+) diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 1f22a30c09637..3ca9e03f4e675 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -264,6 +264,9 @@ extern void suspend_device_irqs(void); extern void resume_device_irqs(void); extern void rearm_wake_irq(unsigned int irq); +extern void init_irq_default_prio(struct irq_desc *desc); +extern int irq_set_priority(unsigned int irq, int priority); + /** * struct irq_affinity_notify - context for notification of IRQ affinity changes * @irq: Interrupt to which notification applies diff --git a/include/linux/irq.h b/include/linux/irq.h index c8293c817646c..e9f8850be0bd0 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -185,6 +185,7 @@ struct irq_data { struct irq_data *parent_data; #endif void *chip_data; + int priority; }; /* diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 54363527feea4..e05660fed2885 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -35,6 +35,7 @@ enum { IRQTF_WARNED, IRQTF_AFFINITY, IRQTF_FORCED_THREAD, + IRQTF_PRIORITY, }; /* diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index 4e3c29bb603c3..341a8a4c84d77 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -112,6 +112,7 @@ static void desc_set_defaults(unsigned int irq, struct irq_desc *desc, int node, desc->irq_data.irq = irq; desc->irq_data.chip = &no_irq_chip; desc->irq_data.chip_data = NULL; + init_irq_default_prio(desc); irq_settings_clr_and_set(desc, ~0, _IRQ_DEFAULT_INIT_FLAGS); irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED); irqd_set(&desc->irq_data, IRQD_IRQ_MASKED); diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 894e4db1fffcc..251363baa4c39 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -35,6 +35,11 @@ static int __init setup_forced_irqthreads(char *arg) early_param("threadirqs", setup_forced_irqthreads); #endif +void init_irq_default_prio(struct irq_desc *desc) +{ + desc->irq_data.priority = MAX_RT_PRIO/2; +} + static void __synchronize_hardirq(struct irq_desc *desc, bool sync_chip) { struct irq_data *irqd = irq_desc_get_irq_data(desc); @@ -140,6 +145,45 @@ void synchronize_irq(unsigned int irq) } EXPORT_SYMBOL(synchronize_irq); +static void +irq_thread_check_priority(struct irq_desc *desc, struct irqaction *action) +{ + struct sched_param param; + + if (!test_and_clear_bit(IRQTF_PRIORITY, &action->thread_flags)) + return; + + param.sched_priority = desc->irq_data.priority; + sched_setscheduler(current, SCHED_FIFO, ¶m); +} + +void irq_set_thread_priority(struct irq_desc *desc) +{ + struct irqaction *action = desc->action; + + while (action) { + if (action->thread) + set_bit(IRQTF_PRIORITY, &action->thread_flags); + action = action->next; + } +} + +int irq_set_priority(unsigned int irq, int priority) +{ + unsigned long flags; + struct irq_desc *desc = irq_to_desc(irq); + + if (!desc) + return -EINVAL; + if (!(priority > 0 && priority < MAX_RT_PRIO)) + return -EINVAL; + raw_spin_lock_irqsave(&desc->lock, flags); + desc->irq_data.priority = priority; + irq_set_thread_priority(desc); + raw_spin_unlock_irqrestore(&desc->lock, flags); + return 0; +} + #ifdef CONFIG_SMP cpumask_var_t irq_default_affinity; @@ -1270,12 +1314,17 @@ static int irq_thread(void *data) init_task_work(&on_exit_work, irq_thread_dtor); task_work_add(current, &on_exit_work, TWA_NONE); + /* optimize jitter for the irq threads that don't change affinity or + * priority dynamically + */ irq_thread_check_affinity(desc, action); + irq_thread_check_priority(desc, action); while (!irq_wait_for_interrupt(action)) { irqreturn_t action_ret; irq_thread_check_affinity(desc, action); + irq_thread_check_priority(desc, action); action_ret = handler_fn(desc, action); if (action_ret == IRQ_WAKE_THREAD) @@ -1448,6 +1497,7 @@ setup_irq_thread(struct irqaction *new, unsigned int irq, bool secondary) * on which the requesting code placed the interrupt. */ set_bit(IRQTF_AFFINITY, &new->thread_flags); + set_bit(IRQTF_PRIORITY, &new->thread_flags); return 0; } @@ -1765,6 +1815,18 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) *old_ptr = new; + /* Set default affinity mask once everything is setup. + * We have to do this after the desc->action is assigned the new action + * just above. If setup_affinity is called before, then the IRQ thread + * associated with the new action does not inherit the core affinity + * specified in /proc/irq//smp_affinity file whereas the IRQ + * threads of the existing action handlers do inherit. + */ + if (!shared) { + irq_setup_affinity(desc); + irq_set_thread_priority(desc); + } + irq_pm_install_action(desc, new); /* Reset broken irq detection when installing new handler */ diff --git a/kernel/irq/proc.c b/kernel/irq/proc.c index ee595ec09778d..b62da8d81f98c 100644 --- a/kernel/irq/proc.c +++ b/kernel/irq/proc.c @@ -5,6 +5,8 @@ * This file contains the /proc/irq/ handling code. */ +#include + #include #include #include @@ -295,6 +297,57 @@ static int irq_spurious_proc_show(struct seq_file *m, void *v) return 0; } +static ssize_t irq_priority_proc_write(struct file *file, + const char __user *user_buf, size_t count, loff_t *pos) +{ + int priority; + int err = 0; + unsigned int irq; + char buffer[20]; + + memset(buffer, 0, sizeof(buffer)); + if (count > sizeof(buffer) - 1) + count = sizeof(buffer) - 1; + if (copy_from_user(buffer, user_buf, count)) { + err = -EFAULT; + goto out; + } + err = kstrtoint(strstrip(buffer), 0, &priority); + if (err) + goto out; + irq = (int)(long)PDE_DATA(file_inode(file)); + err = irq_set_priority(irq, priority); +out: + return err < 0 ? err : count; +} + +static int show_irq_priority(int type, struct seq_file *m, void *v) +{ + struct irq_desc *desc = irq_to_desc((long)m->private); + int prio = desc->irq_data.priority; + + seq_printf(m, "%d\n", prio); + return 0; +} + +static int irq_priority_proc_show(struct seq_file *m, void *v) +{ + return show_irq_priority(0, m, v); +} + +static int irq_priority_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, irq_priority_proc_show, PDE_DATA(inode)); +} + +static const struct proc_ops irq_priority_proc_ops = { + .proc_open = irq_priority_proc_open, + .proc_read = seq_read, + .proc_lseek = seq_lseek, + .proc_release = single_release, + .proc_write = irq_priority_proc_write, +}; + #define MAX_NAMELEN 128 static int name_unique(unsigned int irq, struct irqaction *new_action) @@ -386,6 +439,8 @@ void register_irq_proc(unsigned int irq, struct irq_desc *desc) proc_create_single_data("spurious", 0444, desc->dir, irq_spurious_proc_show, (void *)(long)irq); + proc_create_data("priority", 0600, desc->dir, + &irq_priority_proc_ops, irqp); out_unlock: mutex_unlock(®ister_lock); } @@ -407,6 +462,7 @@ void unregister_irq_proc(unsigned int irq, struct irq_desc *desc) # endif #endif remove_proc_entry("spurious", desc->dir); + remove_proc_entry("priority", desc->dir); sprintf(name, "%u", irq); remove_proc_entry(name, root_irq_dir); From f9440b7c68b8268287060dc92c7b05e2e726610a Mon Sep 17 00:00:00 2001 From: Haris Okanovic Date: Fri, 25 Sep 2015 11:34:42 -0500 Subject: [PATCH 05/84] proc/interrupts: Add polling Implement polling on procfs' "interrupts" file which observes changes to IRQ action handlers. The poll fires each time an action handler is registered or unregistered. This change enables daemons to watch for changes and apply certain system policies relating to IRQ processing. For example, modify execution priority of dedicated IRQ tasks after they're created. include/linux/interrupt.h kernel/irq/manage.c Add change counter for handler registrations and a wait queue to notify tasks on updates. fs/proc/interrupts.c Add polling callback on aforementioned counter and wait queue. Signed-off-by: Haris Okanovic Signed-off-by: Ovidiu-Adrian Vancea Signed-off-by: Brad Mouring Natinst-ReviewBoard-ID: 111860, 163902 [gratian: fixed small rebase conflict] Signed-off-by: Gratian Crisan [bstreiff: un-trivialize from changes in fddda2b7b521 ("proc: introduce proc_create_seq{,_data}") and convert file_operations to proc_ops] Signed-off-by: Brandon Streiff --- fs/proc/interrupts.c | 74 ++++++++++++++++++++++++++++++++++++++- include/linux/interrupt.h | 3 ++ kernel/irq/manage.c | 18 ++++++++++ 3 files changed, 94 insertions(+), 1 deletion(-) diff --git a/fs/proc/interrupts.c b/fs/proc/interrupts.c index cb0edc7cbf092..624eb74af1e3c 100644 --- a/fs/proc/interrupts.c +++ b/fs/proc/interrupts.c @@ -1,10 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include #include #include #include #include +#include /* * /proc/interrupts @@ -34,9 +36,79 @@ static const struct seq_operations int_seq_ops = { .show = show_interrupts }; +struct interrupts_fd_state { + atomic_long_t last_irq_change_count; +}; + +static int interrupts_open(struct inode *inode, struct file *filp) +{ + int res; + struct interrupts_fd_state *privdata; + struct seq_file *sf; + + privdata = kzalloc(sizeof(struct interrupts_fd_state), GFP_KERNEL); + if (!privdata) { + res = -ENOMEM; + goto exit; + } + + res = seq_open(filp, &int_seq_ops); + if (res) { + kfree(privdata); + goto exit; + } + + sf = filp->private_data; + sf->private = privdata; + + atomic_long_set(&(privdata->last_irq_change_count), + get_irq_handler_change_count()); + +exit: + return res; +} + +static int interrupts_release(struct inode *inode, struct file *filp) +{ + struct seq_file *sf = filp->private_data; + + kfree(sf->private); + return seq_release(inode, filp); +} + +static unsigned int interrupts_poll(struct file *filp, + struct poll_table_struct *pt) +{ + unsigned int mask = POLLIN | POLLRDNORM; + long newcount, oldcount; + struct seq_file *sf = filp->private_data; + struct interrupts_fd_state *fds = sf->private; + + /* Register for changes to IRQ handlers */ + poll_wait(filp, &irq_handler_change_wq, pt); + + /* Store new change count in priv data */ + newcount = get_irq_handler_change_count(); + oldcount = atomic_long_xchg( + &(fds->last_irq_change_count), newcount); + + if (newcount != oldcount) + mask |= POLLERR | POLLPRI; + + return mask; +} + +static const struct proc_ops interrupts_proc_ops = { + .proc_open = interrupts_open, + .proc_read = seq_read, + .proc_poll = interrupts_poll, + .proc_lseek = seq_lseek, + .proc_release = interrupts_release, +}; + static int __init proc_interrupts_init(void) { - proc_create_seq("interrupts", 0, NULL, &int_seq_ops); + proc_create("interrupts", 0, NULL, &interrupts_proc_ops); return 0; } fs_initcall(proc_interrupts_init); diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 3ca9e03f4e675..6bb8d18a5acdb 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -774,6 +774,9 @@ extern int early_irq_init(void); extern int arch_probe_nr_irqs(void); extern int arch_early_irq_init(void); +extern long get_irq_handler_change_count(void); +extern wait_queue_head_t irq_handler_change_wq; + /* * We want to know which function is an entrypoint of a hardirq or a softirq. */ diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 251363baa4c39..b4c45e91df55d 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -40,6 +40,22 @@ void init_irq_default_prio(struct irq_desc *desc) desc->irq_data.priority = MAX_RT_PRIO/2; } +static atomic_long_t irq_handler_change_count = ATOMIC_LONG_INIT(0); +DECLARE_WAIT_QUEUE_HEAD(irq_handler_change_wq); + +/* Bump change count and wake up anything waiting on changes to + * IRQ handlers */ +static void __irq_handler_change_event(void) +{ + atomic_long_inc(&irq_handler_change_count); + wake_up(&irq_handler_change_wq); +} + +long get_irq_handler_change_count(void) +{ + return atomic_long_read(&irq_handler_change_count); +} + static void __synchronize_hardirq(struct irq_desc *desc, bool sync_chip) { struct irq_data *irqd = irq_desc_get_irq_data(desc); @@ -1860,6 +1876,7 @@ __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new) register_irq_proc(irq, desc); new->dir = NULL; register_handler_proc(irq, new); + __irq_handler_change_event(); return 0; mismatch: @@ -2038,6 +2055,7 @@ static struct irqaction *__free_irq(struct irq_desc *desc, void *dev_id) irq_chip_pm_put(&desc->irq_data); module_put(desc->owner); kfree(action->secondary); + __irq_handler_change_event(); return action; } From 209ba1587b9f3eb9e10a57a4acdf86ba9578cd1e Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Mon, 16 Dec 2013 10:33:02 -0600 Subject: [PATCH 06/84] nirtfeatures: Added NI RT features driver Added an NI RT features driver. This is an ACPI device that exposes LEDs, switches, and other hardware features of the Smasher controllers. Not all of the proposed features of the device work as expected, and some features may be removed in the future. Development work on this device by the hardware team is currently not a high priority. These issues will be addressed once the hardware team gets back to this device. Signed-off-by: Jeff Westfahl [gratian: fix conflict with 7a6ff4c4cbc3 ("misc: hisi_hikey_usb: Driver to support onboard USB gpio hub on Hikey960")] Signed-off-by: Gratian Crisan [gratian: fix conflict with bb3b6552a5b0 ("staging: hikey9xx: split hi6421v600 irq into a separate driver")] Signed-off-by: Gratian Crisan --- drivers/misc/Kconfig | 31 ++ drivers/misc/Makefile | 1 + drivers/misc/nirtfeatures.c | 888 ++++++++++++++++++++++++++++++++++++ 3 files changed, 920 insertions(+) create mode 100644 drivers/misc/nirtfeatures.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0f5a49fc7c9e0..b19aa85baeec9 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -470,6 +470,37 @@ config HISI_HIKEY_USB switching between the dual-role USB-C port and the USB-A host ports using only one USB controller. +config NI_RT_FEATURES + bool "NI 903x/913x support" + depends on X86 && ACPI + help + This driver exposes LEDs and other features of NI 903x/913x Real-Time + controllers. + + If unsure, say N (but it's safe to say "Y"). + +config NI_LED_PREFIX + string "NI 903x/913x LED prefix" + depends on NI_RT_FEATURES + default "nizynqcpld" + help + This option defines the base name of LEDs exposed by NI_RT_FEATURES. + The default value maintains backwards compatibility with user-space + software that was originally written for ARM (Zynq) hardware. + + If unsure, use the default. + +config NI_HW_REBOOT + bool "Hardware reboot for NI 903x/913x" + depends on NI_RT_FEATURES && X86_64 + default n + help + If enabled, when rebooting an NI 903x/913x, on-board hardware will + be used to reset the system. If not enabled, the controller will do + a normal ACPI reset. + + If unsure, say N. + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index a086197af5447..5d1f171bc7b11 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -59,3 +59,4 @@ obj-$(CONFIG_UACCE) += uacce/ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o +obj-$(CONFIG_NI_RT_FEATURES) += nirtfeatures.o diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c new file mode 100644 index 0000000000000..11f6d1f0679e9 --- /dev/null +++ b/drivers/misc/nirtfeatures.c @@ -0,0 +1,888 @@ +/* + * Copyright (C) 2013 National Instruments 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. + * + * 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 + +#ifdef CONFIG_NI_HW_REBOOT +#include +#endif + +#define MODULE_NAME "nirtfeatures" + +/* Register addresses */ + +#define NIRTF_SIGNATURE 0x00 +#define NIRTF_YEAR 0x01 +#define NIRTF_MONTH 0x02 +#define NIRTF_DAY 0x03 +#define NIRTF_HOUR 0x04 +#define NIRTF_MINUTE 0x05 +#define NIRTF_SCRATCH 0x06 +#define NIRTF_BPINFO 0x07 +#define NIRTF_RAIL_STATUS1 0x08 +#define NIRTF_RAIL_STATUS2 0x09 +#define NIRTF_RESET 0x10 +#define NIRTF_RESET_SOURCE 0x11 +#define NIRTF_PROCESSOR_MODE 0x12 +#define NIRTF_SYSTEM_LEDS 0x20 +#define NIRTF_STATUS_LED_SHIFT1 0x21 +#define NIRTF_STATUS_LED_SHIFT0 0x22 +#define NIRTF_RT_LEDS 0x23 + +#define NIRTF_IO_SIZE 0x40 + +/* Register values */ + +#define NIRTF_BPINFO_ID_MASK 0x07 + +#define NIRTF_BPINFO_ID_MANHATTAN 0 +#define NIRTF_BPINFO_ID_HAMMERHEAD 1 + +#define NIRTF_RESET_RESET_PROCESSOR 0x80 + +#define NIRTF_RESET_SOURCE_SOFT_OFF 0x20 +#define NIRTF_RESET_SOURCE_SOFTWARE 0x10 +#define NIRTF_RESET_SOURCE_WATCHDOG 0x08 +#define NIRTF_RESET_SOURCE_FPGA 0x04 +#define NIRTF_RESET_SOURCE_PROCESSOR 0x02 +#define NIRTF_RESET_SOURCE_BUTTON 0x01 + +#define NIRTF_PROCESSOR_MODE_HARD_BOOT_N 0x20 +#define NIRTF_PROCESSOR_MODE_NO_FPGA 0x10 +#define NIRTF_PROCESSOR_MODE_RECOVERY 0x08 +#define NIRTF_PROCESSOR_MODE_CONSOLE_OUT 0x04 +#define NIRTF_PROCESSOR_MODE_IP_RESET 0x02 +#define NIRTF_PROCESSOR_MODE_SAFE 0x01 + +#define NIRTF_SYSTEM_LEDS_STATUS_RED 0x08 +#define NIRTF_SYSTEM_LEDS_STATUS_YELLOW 0x04 +#define NIRTF_SYSTEM_LEDS_POWER_GREEN 0x02 +#define NIRTF_SYSTEM_LEDS_POWER_YELLOW 0x01 + +#define NIRTF_RT_LEDS_USER2_GREEN 0x08 +#define NIRTF_RT_LEDS_USER2_YELLOW 0x04 +#define NIRTF_RT_LEDS_USER1_GREEN 0x02 +#define NIRTF_RT_LEDS_USER1_YELLOW 0x01 + +/* Structures */ + +struct nirtfeatures { + struct acpi_device *acpi_device; + u16 io_base; + u16 io_size; + spinlock_t lock; + u8 revision[5]; + const char *bpstring; + struct nirtfeatures_led *extra_leds; + unsigned num_extra_leds; +}; + +struct nirtfeatures_led { + struct led_classdev cdev; + struct nirtfeatures *nirtfeatures; + u8 address; + u8 mask; + u8 pattern_hi_addr; + u8 pattern_lo_addr; +}; + +/* sysfs files */ + +static ssize_t nirtfeatures_revision_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + + return sprintf(buf, "20%02X/%02X/%02X %02X:%02X\n", + nirtfeatures->revision[0], nirtfeatures->revision[1], + nirtfeatures->revision[2], nirtfeatures->revision[3], + nirtfeatures->revision[4]); +} + +static DEVICE_ATTR(revision, S_IRUGO, nirtfeatures_revision_get, NULL); + +static ssize_t nirtfeatures_scratch_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + u8 data; + + data = inb(nirtfeatures->io_base + NIRTF_SCRATCH); + + return sprintf(buf, "%02x\n", data); +} + +static ssize_t nirtfeatures_scratch_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + unsigned long tmp; + u8 data; + + if (kstrtoul(buf, 0, &tmp) || (tmp > 0xFF)) + return -EINVAL; + + data = (u8)tmp; + + outb(data, nirtfeatures->io_base + NIRTF_SCRATCH); + + return count; +} + +static DEVICE_ATTR(scratch, S_IRUGO|S_IWUSR, nirtfeatures_scratch_get, + nirtfeatures_scratch_set); + +static ssize_t nirtfeatures_backplane_id_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + + return sprintf(buf, "%s\n", nirtfeatures->bpstring); +} + +static DEVICE_ATTR(backplane_id, S_IRUGO, nirtfeatures_backplane_id_get, NULL); + +static ssize_t nirtfeatures_railstatus1_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + u8 data; + + data = inb(nirtfeatures->io_base + NIRTF_RAIL_STATUS1); + + return sprintf(buf, "%02x\n", data); +} + +static DEVICE_ATTR(railstatus1, S_IRUGO, nirtfeatures_railstatus1_get, NULL); + +static ssize_t nirtfeatures_railstatus2_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + u8 data; + + data = inb(nirtfeatures->io_base + NIRTF_RAIL_STATUS2); + + return sprintf(buf, "%02x\n", data); +} + +static DEVICE_ATTR(railstatus2, S_IRUGO, nirtfeatures_railstatus2_get, NULL); + +static ssize_t nirtfeatures_reset_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + + if (strcmp(buf, "1")) + return -EINVAL; + + outb(NIRTF_RESET_RESET_PROCESSOR, nirtfeatures->io_base + NIRTF_RESET); + + return count; +} + +static DEVICE_ATTR(reset, S_IWUSR, NULL, nirtfeatures_reset_set); + +static ssize_t nirtfeatures_reset_source_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + u8 data; + const char *reset_source; + + data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + + data &= NIRTF_PROCESSOR_MODE_HARD_BOOT_N; + + /* Power-on reset status is in a different register from the other reset + sources, we must check it first. */ + if (!data) { + reset_source = "power-on reset"; + } else { + data = inb(nirtfeatures->io_base + NIRTF_RESET_SOURCE); + + switch (data) { + case NIRTF_RESET_SOURCE_SOFT_OFF: + reset_source = "soft off button"; + break; + case NIRTF_RESET_SOURCE_SOFTWARE: + reset_source = "software"; + break; + case NIRTF_RESET_SOURCE_WATCHDOG: + reset_source = "watchdog"; + break; + case NIRTF_RESET_SOURCE_FPGA: + reset_source = "FPGA"; + break; + case NIRTF_RESET_SOURCE_PROCESSOR: + reset_source = "processor"; + break; + case NIRTF_RESET_SOURCE_BUTTON: + reset_source = "reset button"; + break; + default: + dev_err(&nirtfeatures->acpi_device->dev, + "Unrecognized reset source 0x%02X\n", + data); + reset_source = "unknown"; + break; + } + } + + return sprintf(buf, "%s\n", reset_source); +} + +static DEVICE_ATTR(reset_source, S_IRUGO, nirtfeatures_reset_source_get, NULL); + +static ssize_t nirtfeatures_hard_boot_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + u8 data; + + data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + + data &= NIRTF_PROCESSOR_MODE_HARD_BOOT_N; + + return sprintf(buf, "%s\n", data ? "soft reset" : "power-on reset"); +} + +static ssize_t nirtfeatures_hard_boot_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + u8 data; + + if (strcmp(buf, "1")) + return -EINVAL; + + spin_lock(&nirtfeatures->lock); + + data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + + data |= NIRTF_PROCESSOR_MODE_HARD_BOOT_N; + + outb(data, nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + + spin_unlock(&nirtfeatures->lock); + + return count; +} + +static DEVICE_ATTR(hard_boot, S_IRUGO|S_IWUSR, nirtfeatures_hard_boot_get, + nirtfeatures_hard_boot_set); + +static ssize_t nirtfeatures_no_fpga_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + u8 data; + + data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + + data &= NIRTF_PROCESSOR_MODE_NO_FPGA; + + return sprintf(buf, "%u\n", !!data); +} + +static ssize_t nirtfeatures_no_fpga_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + unsigned long tmp; + u8 data; + + if (kstrtoul(buf, 0, &tmp) || (tmp > 1)) + return -EINVAL; + + spin_lock(&nirtfeatures->lock); + + data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + + if (tmp) + data |= NIRTF_PROCESSOR_MODE_NO_FPGA; + else + data &= ~NIRTF_PROCESSOR_MODE_NO_FPGA; + + outb(data, nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + + spin_unlock(&nirtfeatures->lock); + + return count; +} + +static DEVICE_ATTR(no_fpga, S_IRUGO|S_IWUSR, nirtfeatures_no_fpga_get, + nirtfeatures_no_fpga_set); + +static ssize_t nirtfeatures_recovery_mode_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + u8 data; + + data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + + data &= NIRTF_PROCESSOR_MODE_RECOVERY; + + return sprintf(buf, "%u\n", !!data); +} + +static ssize_t nirtfeatures_recovery_mode_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + unsigned long tmp; + u8 data; + + if (kstrtoul(buf, 0, &tmp) || (tmp > 1)) + return -EINVAL; + + spin_lock(&nirtfeatures->lock); + + data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + + if (tmp) + data |= NIRTF_PROCESSOR_MODE_RECOVERY; + else + data &= ~NIRTF_PROCESSOR_MODE_RECOVERY; + + outb(data, nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + + spin_unlock(&nirtfeatures->lock); + + return count; +} + +static DEVICE_ATTR(recovery_mode, S_IRUGO|S_IWUSR, + nirtfeatures_recovery_mode_get, nirtfeatures_recovery_mode_set); + +static ssize_t nirtfeatures_console_out_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + u8 data; + + data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + + data &= NIRTF_PROCESSOR_MODE_CONSOLE_OUT; + + return sprintf(buf, "%u\n", !!data); +} + +static DEVICE_ATTR(console_out, S_IRUGO, nirtfeatures_console_out_get, NULL); + +static ssize_t nirtfeatures_ip_reset_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + u8 data; + + data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + + data &= NIRTF_PROCESSOR_MODE_IP_RESET; + + return sprintf(buf, "%u\n", !!data); +} + +static DEVICE_ATTR(ip_reset, S_IRUGO, nirtfeatures_ip_reset_get, NULL); + +static ssize_t nirtfeatures_safe_mode_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + u8 data; + + data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + + data &= NIRTF_PROCESSOR_MODE_SAFE; + + return sprintf(buf, "%u\n", !!data); +} + +static DEVICE_ATTR(safe_mode, S_IRUGO, nirtfeatures_safe_mode_get, NULL); + +static ssize_t nirtfeatures_register_dump_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + u8 signature, year, month, day, hour, minute, scratch, bpinfo; + u8 railstatus1, railstatus2, reset, reset_source, processor_mode; + u8 system_leds, status_led_shift1, status_led_shift0, rt_leds; + + signature = inb(nirtfeatures->io_base + NIRTF_SIGNATURE); + year = inb(nirtfeatures->io_base + NIRTF_YEAR); + month = inb(nirtfeatures->io_base + NIRTF_MONTH); + day = inb(nirtfeatures->io_base + NIRTF_DAY); + hour = inb(nirtfeatures->io_base + NIRTF_HOUR); + minute = inb(nirtfeatures->io_base + NIRTF_MINUTE); + scratch = inb(nirtfeatures->io_base + NIRTF_SCRATCH); + bpinfo = inb(nirtfeatures->io_base + NIRTF_BPINFO); + railstatus1 = inb(nirtfeatures->io_base + NIRTF_RAIL_STATUS1); + railstatus2 = inb(nirtfeatures->io_base + NIRTF_RAIL_STATUS2); + reset = inb(nirtfeatures->io_base + NIRTF_RESET); + reset_source = inb(nirtfeatures->io_base + NIRTF_RESET_SOURCE); + processor_mode = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + system_leds = inb(nirtfeatures->io_base + NIRTF_SYSTEM_LEDS); + status_led_shift1 = + inb(nirtfeatures->io_base + NIRTF_STATUS_LED_SHIFT1); + status_led_shift0 = + inb(nirtfeatures->io_base + NIRTF_STATUS_LED_SHIFT0); + rt_leds = inb(nirtfeatures->io_base + NIRTF_RT_LEDS); + + return sprintf(buf, + "Signature: 0x%02X\n" + "Year: 0x%02X\n" + "Month: 0x%02X\n" + "Day: 0x%02X\n" + "Hour: 0x%02X\n" + "Minute: 0x%02X\n" + "Scratch: 0x%02X\n" + "BPInfo: 0x%02X\n" + "Rail status 1: 0x%02X\n" + "Rail status 2: 0x%02X\n" + "Reset: 0x%02X\n" + "Reset source: 0x%02X\n" + "Processor mode: 0x%02X\n" + "System LEDs: 0x%02X\n" + "Status LED shift 1: 0x%02X\n" + "Status LED shift 0: 0x%02X\n" + "RT LEDs: 0x%02X\n", + signature, year, month, day, hour, minute, scratch, + bpinfo, railstatus1, railstatus2, reset, reset_source, + processor_mode, system_leds, status_led_shift1, + status_led_shift0, rt_leds); +} + +static DEVICE_ATTR(register_dump, S_IRUGO, nirtfeatures_register_dump_get, + NULL); + +static const struct attribute *nirtfeatures_attrs[] = { + &dev_attr_revision.attr, + &dev_attr_scratch.attr, + &dev_attr_backplane_id.attr, + &dev_attr_railstatus1.attr, + &dev_attr_railstatus2.attr, + &dev_attr_reset.attr, + &dev_attr_reset_source.attr, + &dev_attr_hard_boot.attr, + &dev_attr_no_fpga.attr, + &dev_attr_recovery_mode.attr, + &dev_attr_console_out.attr, + &dev_attr_ip_reset.attr, + &dev_attr_safe_mode.attr, + &dev_attr_register_dump.attr, + NULL +}; + +/* LEDs */ + +static void nirtfeatures_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct nirtfeatures_led *led = (struct nirtfeatures_led *)led_cdev; + u8 data; + bool on; + u16 pattern; + + on = !!brightness; + pattern = brightness; + + spin_lock(&led->nirtfeatures->lock); + + data = inb(led->nirtfeatures->io_base + led->address); + + data &= ~led->mask; + + if (on) + data |= led->mask; + + outb(data, led->nirtfeatures->io_base + led->address); + + if (led->pattern_hi_addr && led->pattern_lo_addr) { + /* Write the high byte first. */ + outb(pattern >> 8, + led->nirtfeatures->io_base + led->pattern_hi_addr); + outb(pattern & 0xFF, + led->nirtfeatures->io_base + led->pattern_lo_addr); + } + + spin_unlock(&led->nirtfeatures->lock); +} + +static enum led_brightness +nirtfeatures_led_brightness_get(struct led_classdev *led_cdev) +{ + struct nirtfeatures_led *led = (struct nirtfeatures_led *)led_cdev; + u8 data; + + data = inb(led->nirtfeatures->io_base + led->address); + + /* For the yellow status LED, the blink pattern used for brightness + on write is write-only, so we just return on/off for all LEDs. */ + return (data & led->mask) ? LED_FULL : LED_OFF; +} + +static struct nirtfeatures_led nirtfeatures_leds_common[] = { + { + { + .name = CONFIG_NI_LED_PREFIX ":user1:green", + }, + .address = NIRTF_RT_LEDS, + .mask = NIRTF_RT_LEDS_USER1_GREEN, + }, + { + { + .name = CONFIG_NI_LED_PREFIX ":user1:yellow", + }, + .address = NIRTF_RT_LEDS, + .mask = NIRTF_RT_LEDS_USER1_YELLOW, + }, + { + { + .name = CONFIG_NI_LED_PREFIX ":status:red", + }, + .address = NIRTF_SYSTEM_LEDS, + .mask = NIRTF_SYSTEM_LEDS_STATUS_RED, + }, + { + { + .name = CONFIG_NI_LED_PREFIX ":status:yellow", + .max_brightness = 0xFFFF, + }, + .address = NIRTF_SYSTEM_LEDS, + .mask = NIRTF_SYSTEM_LEDS_STATUS_YELLOW, + .pattern_hi_addr = NIRTF_STATUS_LED_SHIFT1, + .pattern_lo_addr = NIRTF_STATUS_LED_SHIFT0, + }, + { + { + .name = CONFIG_NI_LED_PREFIX ":power:green", + }, + .address = NIRTF_SYSTEM_LEDS, + .mask = NIRTF_SYSTEM_LEDS_POWER_GREEN, + }, + { + { + .name = CONFIG_NI_LED_PREFIX ":power:yellow", + }, + .address = NIRTF_SYSTEM_LEDS, + .mask = NIRTF_SYSTEM_LEDS_POWER_YELLOW, + }, +}; + +static struct nirtfeatures_led nirtfeatures_leds_cdaq[] = { + { + { + .name = CONFIG_NI_LED_PREFIX ":user2:green", + }, + .address = NIRTF_RT_LEDS, + .mask = NIRTF_RT_LEDS_USER2_GREEN, + }, + { + { + .name = CONFIG_NI_LED_PREFIX ":user2:yellow", + }, + .address = NIRTF_RT_LEDS, + .mask = NIRTF_RT_LEDS_USER2_YELLOW, + }, +}; + +static int nirtfeatures_create_leds(struct nirtfeatures *nirtfeatures) +{ + int i; + int err; + + for (i = 0; i < ARRAY_SIZE(nirtfeatures_leds_common); ++i) { + + nirtfeatures_leds_common[i].nirtfeatures = nirtfeatures; + + if (0 == nirtfeatures_leds_common[i].cdev.max_brightness) + nirtfeatures_leds_common[i].cdev.max_brightness = 1; + + nirtfeatures_leds_common[i].cdev.brightness_set = + nirtfeatures_led_brightness_set; + + nirtfeatures_leds_common[i].cdev.brightness_get = + nirtfeatures_led_brightness_get; + + err = led_classdev_register(&nirtfeatures->acpi_device->dev, + &nirtfeatures_leds_common[i].cdev); + if (err) + return err; + } + + for (i = 0; i < nirtfeatures->num_extra_leds; ++i) { + + nirtfeatures->extra_leds[i].nirtfeatures = nirtfeatures; + + if (0 == nirtfeatures->extra_leds[i].cdev.max_brightness) + nirtfeatures->extra_leds[i].cdev.max_brightness = 1; + + nirtfeatures->extra_leds[i].cdev.brightness_set = + nirtfeatures_led_brightness_set; + + nirtfeatures->extra_leds[i].cdev.brightness_get = + nirtfeatures_led_brightness_get; + + err = led_classdev_register(&nirtfeatures->acpi_device->dev, + &nirtfeatures->extra_leds[i].cdev); + if (err) + return err; + } + + return 0; +} + +static void nirtfeatures_remove_leds(struct nirtfeatures *nirtfeatures) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(nirtfeatures_leds_common); ++i) + led_classdev_unregister(&nirtfeatures_leds_common[i].cdev); + + for (i = 0; i < nirtfeatures->num_extra_leds; ++i) + led_classdev_unregister(&nirtfeatures->extra_leds[i].cdev); +} + +/* Board specific reboot fixup */ + +#ifdef CONFIG_NI_HW_REBOOT + +static u16 mach_reboot_fixup_io_base; + +void mach_reboot_fixups(void) +{ + int i; + + if (mach_reboot_fixup_io_base) + for (i = 0; i < 10; ++i) { + outb(NIRTF_RESET_RESET_PROCESSOR, + mach_reboot_fixup_io_base + NIRTF_RESET); + udelay(100); + } +} + +#endif + +/* ACPI driver */ + +static acpi_status nirtfeatures_resources(struct acpi_resource *res, void *data) +{ + struct nirtfeatures *nirtfeatures = data; + + switch (res->type) { + case ACPI_RESOURCE_TYPE_IO: + if ((nirtfeatures->io_base != 0) || + (nirtfeatures->io_size != 0)) { + dev_err(&nirtfeatures->acpi_device->dev, + "too many IO resources\n"); + return AE_ERROR; + } + + nirtfeatures->io_base = res->data.io.minimum; + nirtfeatures->io_size = res->data.io.address_length; + + return AE_OK; + + case ACPI_RESOURCE_TYPE_END_TAG: + return AE_OK; + + default: + dev_err(&nirtfeatures->acpi_device->dev, + "unsupported resource type %u\n", + res->type); + return AE_ERROR; + } + + return AE_OK; +} + +static int nirtfeatures_acpi_remove(struct acpi_device *device) +{ + struct nirtfeatures *nirtfeatures = device->driver_data; + + nirtfeatures_remove_leds(nirtfeatures); + + sysfs_remove_files(&nirtfeatures->acpi_device->dev.kobj, + nirtfeatures_attrs); + + if ((nirtfeatures->io_base != 0) && + (nirtfeatures->io_size == NIRTF_IO_SIZE)) + release_region(nirtfeatures->io_base, nirtfeatures->io_size); + + device->driver_data = NULL; + + kfree(nirtfeatures); + + return 0; +} + +static int nirtfeatures_acpi_add(struct acpi_device *device) +{ + struct nirtfeatures *nirtfeatures; + acpi_status acpi_ret; + u8 bpinfo; + int err; + + nirtfeatures = kzalloc(sizeof(*nirtfeatures), GFP_KERNEL); + + if (!nirtfeatures) + return -ENOMEM; + + device->driver_data = nirtfeatures; + + nirtfeatures->acpi_device = device; + + acpi_ret = acpi_walk_resources(device->handle, METHOD_NAME__CRS, + nirtfeatures_resources, nirtfeatures); + + if (ACPI_FAILURE(acpi_ret) || + (nirtfeatures->io_base == 0) || + (nirtfeatures->io_size != NIRTF_IO_SIZE)) { + nirtfeatures_acpi_remove(device); + return -ENODEV; + } + + if (!request_region(nirtfeatures->io_base, nirtfeatures->io_size, + MODULE_NAME)) { + nirtfeatures_acpi_remove(device); + return -EBUSY; + } + +#ifdef CONFIG_NI_HW_REBOOT + mach_reboot_fixup_io_base = nirtfeatures->io_base; + reboot_type = BOOT_KBD; +#endif + + bpinfo = inb(nirtfeatures->io_base + NIRTF_BPINFO); + + bpinfo &= NIRTF_BPINFO_ID_MASK; + + switch (bpinfo) { + case NIRTF_BPINFO_ID_MANHATTAN: + nirtfeatures->bpstring = "Manhattan"; + break; + case NIRTF_BPINFO_ID_HAMMERHEAD: + nirtfeatures->bpstring = "Hammerhead"; + nirtfeatures->extra_leds = nirtfeatures_leds_cdaq; + nirtfeatures->num_extra_leds = + ARRAY_SIZE(nirtfeatures_leds_cdaq); + break; + default: + dev_err(&nirtfeatures->acpi_device->dev, + "Unrecognized backplane type %u\n", + bpinfo); + nirtfeatures_acpi_remove(device); + return -ENODEV; + } + + err = sysfs_create_files(&nirtfeatures->acpi_device->dev.kobj, + nirtfeatures_attrs); + if (0 != err) { + nirtfeatures_acpi_remove(device); + return err; + } + + err = nirtfeatures_create_leds(nirtfeatures); + if (0 != err) { + nirtfeatures_acpi_remove(device); + return err; + } + + spin_lock_init(&nirtfeatures->lock); + + nirtfeatures->revision[0] = inb(nirtfeatures->io_base + NIRTF_YEAR); + nirtfeatures->revision[1] = inb(nirtfeatures->io_base + NIRTF_MONTH); + nirtfeatures->revision[2] = inb(nirtfeatures->io_base + NIRTF_DAY); + nirtfeatures->revision[3] = inb(nirtfeatures->io_base + NIRTF_HOUR); + nirtfeatures->revision[4] = inb(nirtfeatures->io_base + NIRTF_MINUTE); + + dev_info(&nirtfeatures->acpi_device->dev, + "IO range 0x%04X-0x%04X\n", + nirtfeatures->io_base, + nirtfeatures->io_base + nirtfeatures->io_size - 1); + + return 0; +} + +static const struct acpi_device_id nirtfeatures_device_ids[] = { + {"NIC775D", 0}, + {"", 0}, +}; + +static struct acpi_driver nirtfeatures_acpi_driver = { + .name = MODULE_NAME, + .ids = nirtfeatures_device_ids, + .ops = { + .add = nirtfeatures_acpi_add, + .remove = nirtfeatures_acpi_remove, + }, +}; + +static int __init nirtfeatures_init(void) +{ + return acpi_bus_register_driver(&nirtfeatures_acpi_driver); +} + +static void __exit nirtfeatures_exit(void) +{ + acpi_bus_unregister_driver(&nirtfeatures_acpi_driver); +} + +module_init(nirtfeatures_init); +module_exit(nirtfeatures_exit); + +MODULE_DEVICE_TABLE(acpi, nirtfeatures_device_ids); +MODULE_DESCRIPTION("NI RT Features"); +MODULE_AUTHOR("Jeff Westfahl "); +MODULE_LICENSE("GPL"); From 5736cfb8cf1dfbcf2689acda97997c619dd4e0d7 Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Fri, 14 Mar 2014 17:24:18 -0500 Subject: [PATCH 07/84] nirtfeatures: change hard_boot to soft_reset to match our Zynq targets On our Zynq targets, we expose the power-on reset status of the controller via a soft_reset sysfs file. The same underlying bit in the CPLD is exposed as hard_boot on our Smasher targets. In this commit we change the Smasher implementation to match Zynq. Signed-off-by: Jeff Westfahl --- drivers/misc/nirtfeatures.c | 37 ++++++------------------------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 11f6d1f0679e9..0d94e62a5ec92 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -264,9 +264,9 @@ static ssize_t nirtfeatures_reset_source_get(struct device *dev, static DEVICE_ATTR(reset_source, S_IRUGO, nirtfeatures_reset_source_get, NULL); -static ssize_t nirtfeatures_hard_boot_get(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t nirtfeatures_soft_reset_get(struct device *dev, + struct device_attribute *attr, + char *buf) { struct acpi_device *acpi_device = to_acpi_device(dev); struct nirtfeatures *nirtfeatures = acpi_device->driver_data; @@ -276,35 +276,10 @@ static ssize_t nirtfeatures_hard_boot_get(struct device *dev, data &= NIRTF_PROCESSOR_MODE_HARD_BOOT_N; - return sprintf(buf, "%s\n", data ? "soft reset" : "power-on reset"); -} - -static ssize_t nirtfeatures_hard_boot_set(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct acpi_device *acpi_device = to_acpi_device(dev); - struct nirtfeatures *nirtfeatures = acpi_device->driver_data; - u8 data; - - if (strcmp(buf, "1")) - return -EINVAL; - - spin_lock(&nirtfeatures->lock); - - data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); - - data |= NIRTF_PROCESSOR_MODE_HARD_BOOT_N; - - outb(data, nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); - - spin_unlock(&nirtfeatures->lock); - - return count; + return sprintf(buf, "%u\n", !!data); } -static DEVICE_ATTR(hard_boot, S_IRUGO|S_IWUSR, nirtfeatures_hard_boot_get, - nirtfeatures_hard_boot_set); +static DEVICE_ATTR(soft_reset, S_IRUGO, nirtfeatures_soft_reset_get, NULL); static ssize_t nirtfeatures_no_fpga_get(struct device *dev, struct device_attribute *attr, @@ -514,7 +489,7 @@ static const struct attribute *nirtfeatures_attrs[] = { &dev_attr_railstatus2.attr, &dev_attr_reset.attr, &dev_attr_reset_source.attr, - &dev_attr_hard_boot.attr, + &dev_attr_soft_reset.attr, &dev_attr_no_fpga.attr, &dev_attr_recovery_mode.attr, &dev_attr_console_out.attr, From 8f5e7b2248bf6bedfd6028883c9affcaf3a3cfee Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Fri, 14 Mar 2014 16:52:23 -0500 Subject: [PATCH 08/84] nirtfeatures: set HARD_BOOT_N if necessary when the driver loads On systems where FPGA autoload is controlled by software, we need a way to determine if a system has just had power applied. This is necessary to implement the autoload on every power-on feature. Once the FPGA has been autloaded after power-on, software sets a bit in the CPLD so that subsequent (non power-on) resets don't cause the FPGA to be autoloaded again. The CPLD provides the HARD_BOOT_N bit for this purpose. On systems where FPGA autoload is controlled by hardware, such as Smasher, we need to set this bit at some point after power-on so that software can correctly determine the reset state of the controller. Since software has no insight into the status of FPGA autoload on these systems, we can just set this bit if necessary when the driver loads. Signed-off-by: Jeff Westfahl --- drivers/misc/nirtfeatures.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 0d94e62a5ec92..59456011d5d73 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -747,6 +747,7 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) struct nirtfeatures *nirtfeatures; acpi_status acpi_ret; u8 bpinfo; + u8 procmode; int err; nirtfeatures = kzalloc(sizeof(*nirtfeatures), GFP_KERNEL); @@ -822,6 +823,13 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) nirtfeatures->revision[3] = inb(nirtfeatures->io_base + NIRTF_HOUR); nirtfeatures->revision[4] = inb(nirtfeatures->io_base + NIRTF_MINUTE); + procmode = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + + if (!(procmode & NIRTF_PROCESSOR_MODE_HARD_BOOT_N)) { + procmode |= NIRTF_PROCESSOR_MODE_HARD_BOOT_N; + outb(procmode, nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + } + dev_info(&nirtfeatures->acpi_device->dev, "IO range 0x%04X-0x%04X\n", nirtfeatures->io_base, From b0427c55b31535edcc30006cec9672179dba67e8 Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Fri, 14 Mar 2014 17:00:58 -0500 Subject: [PATCH 09/84] nirtfeatures: modify reset_source to match Zynq Changed the strings returned by the reset_source sysfs file to match those returned on Zynq. Changed the algorithm used to determine the reset source to match Zynq. Signed-off-by: Jeff Westfahl (Note that the Smasher CPLD currently returns incorrect values for the reset source in some cases. See CARs 458093 and 458094.) --- drivers/misc/nirtfeatures.c | 50 ++++++++----------------------------- 1 file changed, 11 insertions(+), 39 deletions(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 59456011d5d73..fe0d1ba868ddb 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -211,6 +211,10 @@ static ssize_t nirtfeatures_reset_set(struct device *dev, static DEVICE_ATTR(reset, S_IWUSR, NULL, nirtfeatures_reset_set); +static const char * const nirtfeatures_reset_source_strings[] = { + "button", "processor", "fpga", "watchdog", "software", "softoff", +}; + static ssize_t nirtfeatures_reset_source_get(struct device *dev, struct device_attribute *attr, char *buf) @@ -218,48 +222,16 @@ static ssize_t nirtfeatures_reset_source_get(struct device *dev, struct acpi_device *acpi_device = to_acpi_device(dev); struct nirtfeatures *nirtfeatures = acpi_device->driver_data; u8 data; - const char *reset_source; + int i; - data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + data = inb(nirtfeatures->io_base + NIRTF_RESET_SOURCE); - data &= NIRTF_PROCESSOR_MODE_HARD_BOOT_N; - - /* Power-on reset status is in a different register from the other reset - sources, we must check it first. */ - if (!data) { - reset_source = "power-on reset"; - } else { - data = inb(nirtfeatures->io_base + NIRTF_RESET_SOURCE); - - switch (data) { - case NIRTF_RESET_SOURCE_SOFT_OFF: - reset_source = "soft off button"; - break; - case NIRTF_RESET_SOURCE_SOFTWARE: - reset_source = "software"; - break; - case NIRTF_RESET_SOURCE_WATCHDOG: - reset_source = "watchdog"; - break; - case NIRTF_RESET_SOURCE_FPGA: - reset_source = "FPGA"; - break; - case NIRTF_RESET_SOURCE_PROCESSOR: - reset_source = "processor"; - break; - case NIRTF_RESET_SOURCE_BUTTON: - reset_source = "reset button"; - break; - default: - dev_err(&nirtfeatures->acpi_device->dev, - "Unrecognized reset source 0x%02X\n", - data); - reset_source = "unknown"; - break; - } - } + for (i = 0; i < ARRAY_SIZE(nirtfeatures_reset_source_strings); i++) + if ((1 << i) & data) + return sprintf(buf, "%s\n", + nirtfeatures_reset_source_strings[i]); - return sprintf(buf, "%s\n", reset_source); + return sprintf(buf, "poweron\n"); } static DEVICE_ATTR(reset_source, S_IRUGO, nirtfeatures_reset_source_get, NULL); From f2afde9cf1d360087e56eac7e0143e05adc3c16e Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Fri, 14 Mar 2014 17:12:01 -0500 Subject: [PATCH 10/84] nirtfeatures: restructure init function to close holes In nirtfeatures_acpi_add, we create several sysfs files. As currently implemented, there is a window where access to a sysfs file may cause our spinlock to be used before it's been initialized, or may cause us to write an incorrect value to an I/O port. We can close this window by moving the creation of the sysfs files closer to the end of the function. Signed-off-by: Jeff Westfahl --- drivers/misc/nirtfeatures.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index fe0d1ba868ddb..860aca2feca17 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -774,19 +774,6 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) return -ENODEV; } - err = sysfs_create_files(&nirtfeatures->acpi_device->dev.kobj, - nirtfeatures_attrs); - if (0 != err) { - nirtfeatures_acpi_remove(device); - return err; - } - - err = nirtfeatures_create_leds(nirtfeatures); - if (0 != err) { - nirtfeatures_acpi_remove(device); - return err; - } - spin_lock_init(&nirtfeatures->lock); nirtfeatures->revision[0] = inb(nirtfeatures->io_base + NIRTF_YEAR); @@ -802,6 +789,19 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) outb(procmode, nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); } + err = sysfs_create_files(&nirtfeatures->acpi_device->dev.kobj, + nirtfeatures_attrs); + if (0 != err) { + nirtfeatures_acpi_remove(device); + return err; + } + + err = nirtfeatures_create_leds(nirtfeatures); + if (0 != err) { + nirtfeatures_acpi_remove(device); + return err; + } + dev_info(&nirtfeatures->acpi_device->dev, "IO range 0x%04X-0x%04X\n", nirtfeatures->io_base, From 391518c564ce40f492fd362bd90bb577c80b87ad Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Fri, 14 Mar 2014 17:14:42 -0500 Subject: [PATCH 11/84] nirtfeatures: add LOCK, DEBUG_SWITCH, and GP_BUTTON registers The Smasher CPLD has recently exposed some new registers. In this commit we display the values of these registers in the output of the register_dump sysfs file. Signed-off-by: Jeff Westfahl --- drivers/misc/nirtfeatures.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 860aca2feca17..bac07b2015ce1 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -35,6 +35,7 @@ #define NIRTF_BPINFO 0x07 #define NIRTF_RAIL_STATUS1 0x08 #define NIRTF_RAIL_STATUS2 0x09 +#define NIRTF_LOCK 0x0F #define NIRTF_RESET 0x10 #define NIRTF_RESET_SOURCE 0x11 #define NIRTF_PROCESSOR_MODE 0x12 @@ -42,6 +43,8 @@ #define NIRTF_STATUS_LED_SHIFT1 0x21 #define NIRTF_STATUS_LED_SHIFT0 0x22 #define NIRTF_RT_LEDS 0x23 +#define NIRTF_DEBUG_SWITCH 0x30 +#define NIRTF_GP_BUTTON 0x31 #define NIRTF_IO_SIZE 0x40 @@ -403,8 +406,9 @@ static ssize_t nirtfeatures_register_dump_get(struct device *dev, struct acpi_device *acpi_device = to_acpi_device(dev); struct nirtfeatures *nirtfeatures = acpi_device->driver_data; u8 signature, year, month, day, hour, minute, scratch, bpinfo; - u8 railstatus1, railstatus2, reset, reset_source, processor_mode; + u8 railstatus1, railstatus2, lock, reset, reset_source, processor_mode; u8 system_leds, status_led_shift1, status_led_shift0, rt_leds; + u8 debug_switch, gp_button; signature = inb(nirtfeatures->io_base + NIRTF_SIGNATURE); year = inb(nirtfeatures->io_base + NIRTF_YEAR); @@ -416,6 +420,7 @@ static ssize_t nirtfeatures_register_dump_get(struct device *dev, bpinfo = inb(nirtfeatures->io_base + NIRTF_BPINFO); railstatus1 = inb(nirtfeatures->io_base + NIRTF_RAIL_STATUS1); railstatus2 = inb(nirtfeatures->io_base + NIRTF_RAIL_STATUS2); + lock = inb(nirtfeatures->io_base + NIRTF_LOCK); reset = inb(nirtfeatures->io_base + NIRTF_RESET); reset_source = inb(nirtfeatures->io_base + NIRTF_RESET_SOURCE); processor_mode = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); @@ -425,6 +430,8 @@ static ssize_t nirtfeatures_register_dump_get(struct device *dev, status_led_shift0 = inb(nirtfeatures->io_base + NIRTF_STATUS_LED_SHIFT0); rt_leds = inb(nirtfeatures->io_base + NIRTF_RT_LEDS); + debug_switch = inb(nirtfeatures->io_base + NIRTF_DEBUG_SWITCH); + gp_button = inb(nirtfeatures->io_base + NIRTF_GP_BUTTON); return sprintf(buf, "Signature: 0x%02X\n" @@ -437,17 +444,21 @@ static ssize_t nirtfeatures_register_dump_get(struct device *dev, "BPInfo: 0x%02X\n" "Rail status 1: 0x%02X\n" "Rail status 2: 0x%02X\n" + "Lock: 0x%02X\n" "Reset: 0x%02X\n" "Reset source: 0x%02X\n" "Processor mode: 0x%02X\n" "System LEDs: 0x%02X\n" "Status LED shift 1: 0x%02X\n" "Status LED shift 0: 0x%02X\n" - "RT LEDs: 0x%02X\n", + "RT LEDs: 0x%02X\n" + "Debug switch: 0x%02X\n" + "GP button: 0x%02X\n", signature, year, month, day, hour, minute, scratch, - bpinfo, railstatus1, railstatus2, reset, reset_source, - processor_mode, system_leds, status_led_shift1, - status_led_shift0, rt_leds); + bpinfo, railstatus1, railstatus2, lock, reset, + reset_source, processor_mode, system_leds, + status_led_shift1, status_led_shift0, rt_leds, + debug_switch, gp_button); } static DEVICE_ATTR(register_dump, S_IRUGO, nirtfeatures_register_dump_get, From 684103b144974641550d704938f7677640c1f39a Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Fri, 21 Mar 2014 15:42:17 -0500 Subject: [PATCH 12/84] nirtfeatures: Change Hammerhead ID to match hardware When we built Hammerhead, we used ID 4 instead of 1. We don't want to rework all of the boards to match the documentation, so we're just changing the documentation and driver to match what we built. Signed-off-by: Jeff Westfahl --- drivers/misc/nirtfeatures.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index bac07b2015ce1..9dcfbe2973ad7 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -53,7 +53,7 @@ #define NIRTF_BPINFO_ID_MASK 0x07 #define NIRTF_BPINFO_ID_MANHATTAN 0 -#define NIRTF_BPINFO_ID_HAMMERHEAD 1 +#define NIRTF_BPINFO_ID_HAMMERHEAD 4 #define NIRTF_RESET_RESET_PROCESSOR 0x80 From 4126f88445016a52c33a25cb3b2733910d1fe6a4 Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Tue, 1 Apr 2014 13:37:37 -0500 Subject: [PATCH 13/84] nirtfeatures: support for Winghead variant Signed-off-by: Jeff Westfahl --- drivers/misc/nirtfeatures.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 9dcfbe2973ad7..33004479fc5a5 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -54,6 +54,7 @@ #define NIRTF_BPINFO_ID_MANHATTAN 0 #define NIRTF_BPINFO_ID_HAMMERHEAD 4 +#define NIRTF_BPINFO_ID_WINGHEAD 5 #define NIRTF_RESET_RESET_PROCESSOR 0x80 @@ -777,6 +778,12 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) nirtfeatures->num_extra_leds = ARRAY_SIZE(nirtfeatures_leds_cdaq); break; + case NIRTF_BPINFO_ID_WINGHEAD: + nirtfeatures->bpstring = "Winghead"; + nirtfeatures->extra_leds = nirtfeatures_leds_cdaq; + nirtfeatures->num_extra_leds = + ARRAY_SIZE(nirtfeatures_leds_cdaq); + break; default: dev_err(&nirtfeatures->acpi_device->dev, "Unrecognized backplane type %u\n", From d688a4e33a26e56fdbf6ae102af01c3709421d50 Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Tue, 1 Apr 2014 13:58:01 -0500 Subject: [PATCH 14/84] nirtfeatures: support latest CPLD The existing recovery_mode and no_fpga bits are now read only. A new bit, no_fpga_sw, exists for software to tell the CPLD to assert NO_FPGA at the next reset. Signed-off-by: Jeff Westfahl --- drivers/misc/nirtfeatures.c | 85 +++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 47 deletions(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 33004479fc5a5..3c494a1954cb5 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -65,6 +65,7 @@ #define NIRTF_RESET_SOURCE_PROCESSOR 0x02 #define NIRTF_RESET_SOURCE_BUTTON 0x01 +#define NIRTF_PROCESSOR_MODE_NO_FPGA_SW 0x40 #define NIRTF_PROCESSOR_MODE_HARD_BOOT_N 0x20 #define NIRTF_PROCESSOR_MODE_NO_FPGA 0x10 #define NIRTF_PROCESSOR_MODE_RECOVERY 0x08 @@ -240,7 +241,7 @@ static ssize_t nirtfeatures_reset_source_get(struct device *dev, static DEVICE_ATTR(reset_source, S_IRUGO, nirtfeatures_reset_source_get, NULL); -static ssize_t nirtfeatures_soft_reset_get(struct device *dev, +static ssize_t nirtfeatures_no_fpga_sw_get(struct device *dev, struct device_attribute *attr, char *buf) { @@ -250,31 +251,14 @@ static ssize_t nirtfeatures_soft_reset_get(struct device *dev, data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); - data &= NIRTF_PROCESSOR_MODE_HARD_BOOT_N; + data &= NIRTF_PROCESSOR_MODE_NO_FPGA_SW; return sprintf(buf, "%u\n", !!data); } -static DEVICE_ATTR(soft_reset, S_IRUGO, nirtfeatures_soft_reset_get, NULL); - -static ssize_t nirtfeatures_no_fpga_get(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct acpi_device *acpi_device = to_acpi_device(dev); - struct nirtfeatures *nirtfeatures = acpi_device->driver_data; - u8 data; - - data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); - - data &= NIRTF_PROCESSOR_MODE_NO_FPGA; - - return sprintf(buf, "%u\n", !!data); -} - -static ssize_t nirtfeatures_no_fpga_set(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t nirtfeatures_no_fpga_sw_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { struct acpi_device *acpi_device = to_acpi_device(dev); struct nirtfeatures *nirtfeatures = acpi_device->driver_data; @@ -289,9 +273,9 @@ static ssize_t nirtfeatures_no_fpga_set(struct device *dev, data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); if (tmp) - data |= NIRTF_PROCESSOR_MODE_NO_FPGA; + data |= NIRTF_PROCESSOR_MODE_NO_FPGA_SW; else - data &= ~NIRTF_PROCESSOR_MODE_NO_FPGA; + data &= ~NIRTF_PROCESSOR_MODE_NO_FPGA_SW; outb(data, nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); @@ -300,12 +284,12 @@ static ssize_t nirtfeatures_no_fpga_set(struct device *dev, return count; } -static DEVICE_ATTR(no_fpga, S_IRUGO|S_IWUSR, nirtfeatures_no_fpga_get, - nirtfeatures_no_fpga_set); +static DEVICE_ATTR(no_fpga_sw, S_IRUGO|S_IWUSR, nirtfeatures_no_fpga_sw_get, + nirtfeatures_no_fpga_sw_set); -static ssize_t nirtfeatures_recovery_mode_get(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t nirtfeatures_soft_reset_get(struct device *dev, + struct device_attribute *attr, + char *buf) { struct acpi_device *acpi_device = to_acpi_device(dev); struct nirtfeatures *nirtfeatures = acpi_device->driver_data; @@ -313,41 +297,47 @@ static ssize_t nirtfeatures_recovery_mode_get(struct device *dev, data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); - data &= NIRTF_PROCESSOR_MODE_RECOVERY; + data &= NIRTF_PROCESSOR_MODE_HARD_BOOT_N; return sprintf(buf, "%u\n", !!data); } -static ssize_t nirtfeatures_recovery_mode_set(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static DEVICE_ATTR(soft_reset, S_IRUGO, nirtfeatures_soft_reset_get, NULL); + +static ssize_t nirtfeatures_no_fpga_get(struct device *dev, + struct device_attribute *attr, + char *buf) { struct acpi_device *acpi_device = to_acpi_device(dev); struct nirtfeatures *nirtfeatures = acpi_device->driver_data; - unsigned long tmp; u8 data; - if (kstrtoul(buf, 0, &tmp) || (tmp > 1)) - return -EINVAL; + data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); - spin_lock(&nirtfeatures->lock); + data &= NIRTF_PROCESSOR_MODE_NO_FPGA; - data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + return sprintf(buf, "%u\n", !!data); +} - if (tmp) - data |= NIRTF_PROCESSOR_MODE_RECOVERY; - else - data &= ~NIRTF_PROCESSOR_MODE_RECOVERY; +static DEVICE_ATTR(no_fpga, S_IRUGO, nirtfeatures_no_fpga_get, NULL); - outb(data, nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); +static ssize_t nirtfeatures_recovery_mode_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct nirtfeatures *nirtfeatures = acpi_device->driver_data; + u8 data; - spin_unlock(&nirtfeatures->lock); + data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); - return count; + data &= NIRTF_PROCESSOR_MODE_RECOVERY; + + return sprintf(buf, "%u\n", !!data); } -static DEVICE_ATTR(recovery_mode, S_IRUGO|S_IWUSR, - nirtfeatures_recovery_mode_get, nirtfeatures_recovery_mode_set); +static DEVICE_ATTR(recovery_mode, S_IRUGO, + nirtfeatures_recovery_mode_get, NULL); static ssize_t nirtfeatures_console_out_get(struct device *dev, struct device_attribute *attr, @@ -473,6 +463,7 @@ static const struct attribute *nirtfeatures_attrs[] = { &dev_attr_railstatus2.attr, &dev_attr_reset.attr, &dev_attr_reset_source.attr, + &dev_attr_no_fpga_sw.attr, &dev_attr_soft_reset.attr, &dev_attr_no_fpga.attr, &dev_attr_recovery_mode.attr, From f638b6060eada1aa812da92907dbc5376f342a30 Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Thu, 10 Apr 2014 16:56:04 -0500 Subject: [PATCH 15/84] nirtfeatures: don't bail out on unrecognized ID The driver currently returns an error from init if it doesn't recognize the backplane ID. This causes the kernel to hang on boot. Although this is probably a bug somewhere else in the kernel, there's no real benefit to returning an error in this case. It's sufficient to print an error message to the console and return "Unknown" as the backplane ID. Signed-off-by: Jeff Westfahl --- drivers/misc/nirtfeatures.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 3c494a1954cb5..e5d3fbb3c5aec 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -779,8 +779,8 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) dev_err(&nirtfeatures->acpi_device->dev, "Unrecognized backplane type %u\n", bpinfo); - nirtfeatures_acpi_remove(device); - return -ENODEV; + nirtfeatures->bpstring = "Unknown"; + break; } spin_lock_init(&nirtfeatures->lock); From 9a4a45e170177731fa5b70a22f7644d15d9c9768 Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Thu, 10 Apr 2014 16:59:41 -0500 Subject: [PATCH 16/84] nirtfeatures: BIOS writes HARD_BOOT_N, so driver doesn't need to The BIOS will set the HARD_BOOT_N bit at post, so the driver no longer needs to do this. Signed-off-by: Jeff Westfahl --- drivers/misc/nirtfeatures.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index e5d3fbb3c5aec..439d72a1f3beb 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -722,7 +722,6 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) struct nirtfeatures *nirtfeatures; acpi_status acpi_ret; u8 bpinfo; - u8 procmode; int err; nirtfeatures = kzalloc(sizeof(*nirtfeatures), GFP_KERNEL); @@ -791,13 +790,6 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) nirtfeatures->revision[3] = inb(nirtfeatures->io_base + NIRTF_HOUR); nirtfeatures->revision[4] = inb(nirtfeatures->io_base + NIRTF_MINUTE); - procmode = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); - - if (!(procmode & NIRTF_PROCESSOR_MODE_HARD_BOOT_N)) { - procmode |= NIRTF_PROCESSOR_MODE_HARD_BOOT_N; - outb(procmode, nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); - } - err = sysfs_create_files(&nirtfeatures->acpi_device->dev.kobj, nirtfeatures_attrs); if (0 != err) { From 7b28dbacbffdddba385f6e0eb0c421930337f52e Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Fri, 11 Apr 2014 09:01:06 -0500 Subject: [PATCH 17/84] nirtfeatures: remove unused NI_HW_REBOOT config option Remove the unused NI_HW_REBOOT config option. We're not going to use this. Signed-off-by: Jeff Westfahl --- drivers/misc/Kconfig | 11 ----------- drivers/misc/nirtfeatures.c | 29 ----------------------------- 2 files changed, 40 deletions(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index b19aa85baeec9..de63d1db0ad90 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -490,17 +490,6 @@ config NI_LED_PREFIX If unsure, use the default. -config NI_HW_REBOOT - bool "Hardware reboot for NI 903x/913x" - depends on NI_RT_FEATURES && X86_64 - default n - help - If enabled, when rebooting an NI 903x/913x, on-board hardware will - be used to reset the system. If not enabled, the controller will do - a normal ACPI reset. - - If unsure, say N. - source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 439d72a1f3beb..c2076f0f5f6ad 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -17,10 +17,6 @@ #include #include -#ifdef CONFIG_NI_HW_REBOOT -#include -#endif - #define MODULE_NAME "nirtfeatures" /* Register addresses */ @@ -644,26 +640,6 @@ static void nirtfeatures_remove_leds(struct nirtfeatures *nirtfeatures) led_classdev_unregister(&nirtfeatures->extra_leds[i].cdev); } -/* Board specific reboot fixup */ - -#ifdef CONFIG_NI_HW_REBOOT - -static u16 mach_reboot_fixup_io_base; - -void mach_reboot_fixups(void) -{ - int i; - - if (mach_reboot_fixup_io_base) - for (i = 0; i < 10; ++i) { - outb(NIRTF_RESET_RESET_PROCESSOR, - mach_reboot_fixup_io_base + NIRTF_RESET); - udelay(100); - } -} - -#endif - /* ACPI driver */ static acpi_status nirtfeatures_resources(struct acpi_resource *res, void *data) @@ -749,11 +725,6 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) return -EBUSY; } -#ifdef CONFIG_NI_HW_REBOOT - mach_reboot_fixup_io_base = nirtfeatures->io_base; - reboot_type = BOOT_KBD; -#endif - bpinfo = inb(nirtfeatures->io_base + NIRTF_BPINFO); bpinfo &= NIRTF_BPINFO_ID_MASK; From 37ad9a854abedc3405f42415a0d439ece91d90ef Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Fri, 11 Apr 2014 10:09:59 -0500 Subject: [PATCH 18/84] nirtfeatures: update to latest CPLD and remove debugging features Updated registers to match the latest CPLD documentation, removed several sysfs files that were only used for development debugging, and removed several now unused constants. Signed-off-by: Jeff Westfahl --- drivers/misc/nirtfeatures.c | 211 +++++++----------------------------- 1 file changed, 39 insertions(+), 172 deletions(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index c2076f0f5f6ad..51d7bdfac67cf 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -21,53 +21,36 @@ /* Register addresses */ -#define NIRTF_SIGNATURE 0x00 #define NIRTF_YEAR 0x01 #define NIRTF_MONTH 0x02 #define NIRTF_DAY 0x03 #define NIRTF_HOUR 0x04 #define NIRTF_MINUTE 0x05 #define NIRTF_SCRATCH 0x06 -#define NIRTF_BPINFO 0x07 -#define NIRTF_RAIL_STATUS1 0x08 -#define NIRTF_RAIL_STATUS2 0x09 -#define NIRTF_LOCK 0x0F -#define NIRTF_RESET 0x10 -#define NIRTF_RESET_SOURCE 0x11 -#define NIRTF_PROCESSOR_MODE 0x12 +#define NIRTF_PLATFORM_MISC 0x07 +#define NIRTF_PROC_RESET_SOURCE 0x11 +#define NIRTF_CONTROLLER_MODE 0x12 #define NIRTF_SYSTEM_LEDS 0x20 #define NIRTF_STATUS_LED_SHIFT1 0x21 #define NIRTF_STATUS_LED_SHIFT0 0x22 #define NIRTF_RT_LEDS 0x23 -#define NIRTF_DEBUG_SWITCH 0x30 -#define NIRTF_GP_BUTTON 0x31 #define NIRTF_IO_SIZE 0x40 /* Register values */ -#define NIRTF_BPINFO_ID_MASK 0x07 +#define NIRTF_PLATFORM_MISC_ID_MASK 0x07 +#define NIRTF_PLATFORM_MISC_ID_MANHATTAN 0 +#define NIRTF_PLATFORM_MISC_ID_HAMMERHEAD 4 +#define NIRTF_PLATFORM_MISC_ID_WINGHEAD 5 -#define NIRTF_BPINFO_ID_MANHATTAN 0 -#define NIRTF_BPINFO_ID_HAMMERHEAD 4 -#define NIRTF_BPINFO_ID_WINGHEAD 5 - -#define NIRTF_RESET_RESET_PROCESSOR 0x80 - -#define NIRTF_RESET_SOURCE_SOFT_OFF 0x20 -#define NIRTF_RESET_SOURCE_SOFTWARE 0x10 -#define NIRTF_RESET_SOURCE_WATCHDOG 0x08 -#define NIRTF_RESET_SOURCE_FPGA 0x04 -#define NIRTF_RESET_SOURCE_PROCESSOR 0x02 -#define NIRTF_RESET_SOURCE_BUTTON 0x01 - -#define NIRTF_PROCESSOR_MODE_NO_FPGA_SW 0x40 -#define NIRTF_PROCESSOR_MODE_HARD_BOOT_N 0x20 -#define NIRTF_PROCESSOR_MODE_NO_FPGA 0x10 -#define NIRTF_PROCESSOR_MODE_RECOVERY 0x08 -#define NIRTF_PROCESSOR_MODE_CONSOLE_OUT 0x04 -#define NIRTF_PROCESSOR_MODE_IP_RESET 0x02 -#define NIRTF_PROCESSOR_MODE_SAFE 0x01 +#define NIRTF_CONTROLLER_MODE_NO_FPGA_SW 0x40 +#define NIRTF_CONTROLLER_MODE_HARD_BOOT_N 0x20 +#define NIRTF_CONTROLLER_MODE_NO_FPGA 0x10 +#define NIRTF_CONTROLLER_MODE_RECOVERY 0x08 +#define NIRTF_CONTROLLER_MODE_CONSOLE_OUT 0x04 +#define NIRTF_CONTROLLER_MODE_IP_RESET 0x02 +#define NIRTF_CONTROLLER_MODE_SAFE 0x01 #define NIRTF_SYSTEM_LEDS_STATUS_RED 0x08 #define NIRTF_SYSTEM_LEDS_STATUS_YELLOW 0x04 @@ -165,55 +148,8 @@ static ssize_t nirtfeatures_backplane_id_get(struct device *dev, static DEVICE_ATTR(backplane_id, S_IRUGO, nirtfeatures_backplane_id_get, NULL); -static ssize_t nirtfeatures_railstatus1_get(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct acpi_device *acpi_device = to_acpi_device(dev); - struct nirtfeatures *nirtfeatures = acpi_device->driver_data; - u8 data; - - data = inb(nirtfeatures->io_base + NIRTF_RAIL_STATUS1); - - return sprintf(buf, "%02x\n", data); -} - -static DEVICE_ATTR(railstatus1, S_IRUGO, nirtfeatures_railstatus1_get, NULL); - -static ssize_t nirtfeatures_railstatus2_get(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct acpi_device *acpi_device = to_acpi_device(dev); - struct nirtfeatures *nirtfeatures = acpi_device->driver_data; - u8 data; - - data = inb(nirtfeatures->io_base + NIRTF_RAIL_STATUS2); - - return sprintf(buf, "%02x\n", data); -} - -static DEVICE_ATTR(railstatus2, S_IRUGO, nirtfeatures_railstatus2_get, NULL); - -static ssize_t nirtfeatures_reset_set(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct acpi_device *acpi_device = to_acpi_device(dev); - struct nirtfeatures *nirtfeatures = acpi_device->driver_data; - - if (strcmp(buf, "1")) - return -EINVAL; - - outb(NIRTF_RESET_RESET_PROCESSOR, nirtfeatures->io_base + NIRTF_RESET); - - return count; -} - -static DEVICE_ATTR(reset, S_IWUSR, NULL, nirtfeatures_reset_set); - static const char * const nirtfeatures_reset_source_strings[] = { - "button", "processor", "fpga", "watchdog", "software", "softoff", + "button", "processor", "fpga", "watchdog", "software", }; static ssize_t nirtfeatures_reset_source_get(struct device *dev, @@ -225,7 +161,7 @@ static ssize_t nirtfeatures_reset_source_get(struct device *dev, u8 data; int i; - data = inb(nirtfeatures->io_base + NIRTF_RESET_SOURCE); + data = inb(nirtfeatures->io_base + NIRTF_PROC_RESET_SOURCE); for (i = 0; i < ARRAY_SIZE(nirtfeatures_reset_source_strings); i++) if ((1 << i) & data) @@ -245,9 +181,9 @@ static ssize_t nirtfeatures_no_fpga_sw_get(struct device *dev, struct nirtfeatures *nirtfeatures = acpi_device->driver_data; u8 data; - data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + data = inb(nirtfeatures->io_base + NIRTF_CONTROLLER_MODE); - data &= NIRTF_PROCESSOR_MODE_NO_FPGA_SW; + data &= NIRTF_CONTROLLER_MODE_NO_FPGA_SW; return sprintf(buf, "%u\n", !!data); } @@ -266,14 +202,14 @@ static ssize_t nirtfeatures_no_fpga_sw_set(struct device *dev, spin_lock(&nirtfeatures->lock); - data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + data = inb(nirtfeatures->io_base + NIRTF_CONTROLLER_MODE); if (tmp) - data |= NIRTF_PROCESSOR_MODE_NO_FPGA_SW; + data |= NIRTF_CONTROLLER_MODE_NO_FPGA_SW; else - data &= ~NIRTF_PROCESSOR_MODE_NO_FPGA_SW; + data &= ~NIRTF_CONTROLLER_MODE_NO_FPGA_SW; - outb(data, nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + outb(data, nirtfeatures->io_base + NIRTF_CONTROLLER_MODE); spin_unlock(&nirtfeatures->lock); @@ -291,9 +227,9 @@ static ssize_t nirtfeatures_soft_reset_get(struct device *dev, struct nirtfeatures *nirtfeatures = acpi_device->driver_data; u8 data; - data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + data = inb(nirtfeatures->io_base + NIRTF_CONTROLLER_MODE); - data &= NIRTF_PROCESSOR_MODE_HARD_BOOT_N; + data &= NIRTF_CONTROLLER_MODE_HARD_BOOT_N; return sprintf(buf, "%u\n", !!data); } @@ -308,9 +244,9 @@ static ssize_t nirtfeatures_no_fpga_get(struct device *dev, struct nirtfeatures *nirtfeatures = acpi_device->driver_data; u8 data; - data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + data = inb(nirtfeatures->io_base + NIRTF_CONTROLLER_MODE); - data &= NIRTF_PROCESSOR_MODE_NO_FPGA; + data &= NIRTF_CONTROLLER_MODE_NO_FPGA; return sprintf(buf, "%u\n", !!data); } @@ -325,9 +261,9 @@ static ssize_t nirtfeatures_recovery_mode_get(struct device *dev, struct nirtfeatures *nirtfeatures = acpi_device->driver_data; u8 data; - data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + data = inb(nirtfeatures->io_base + NIRTF_CONTROLLER_MODE); - data &= NIRTF_PROCESSOR_MODE_RECOVERY; + data &= NIRTF_CONTROLLER_MODE_RECOVERY; return sprintf(buf, "%u\n", !!data); } @@ -343,9 +279,9 @@ static ssize_t nirtfeatures_console_out_get(struct device *dev, struct nirtfeatures *nirtfeatures = acpi_device->driver_data; u8 data; - data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + data = inb(nirtfeatures->io_base + NIRTF_CONTROLLER_MODE); - data &= NIRTF_PROCESSOR_MODE_CONSOLE_OUT; + data &= NIRTF_CONTROLLER_MODE_CONSOLE_OUT; return sprintf(buf, "%u\n", !!data); } @@ -360,9 +296,9 @@ static ssize_t nirtfeatures_ip_reset_get(struct device *dev, struct nirtfeatures *nirtfeatures = acpi_device->driver_data; u8 data; - data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + data = inb(nirtfeatures->io_base + NIRTF_CONTROLLER_MODE); - data &= NIRTF_PROCESSOR_MODE_IP_RESET; + data &= NIRTF_CONTROLLER_MODE_IP_RESET; return sprintf(buf, "%u\n", !!data); } @@ -377,87 +313,19 @@ static ssize_t nirtfeatures_safe_mode_get(struct device *dev, struct nirtfeatures *nirtfeatures = acpi_device->driver_data; u8 data; - data = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); + data = inb(nirtfeatures->io_base + NIRTF_CONTROLLER_MODE); - data &= NIRTF_PROCESSOR_MODE_SAFE; + data &= NIRTF_CONTROLLER_MODE_SAFE; return sprintf(buf, "%u\n", !!data); } static DEVICE_ATTR(safe_mode, S_IRUGO, nirtfeatures_safe_mode_get, NULL); -static ssize_t nirtfeatures_register_dump_get(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct acpi_device *acpi_device = to_acpi_device(dev); - struct nirtfeatures *nirtfeatures = acpi_device->driver_data; - u8 signature, year, month, day, hour, minute, scratch, bpinfo; - u8 railstatus1, railstatus2, lock, reset, reset_source, processor_mode; - u8 system_leds, status_led_shift1, status_led_shift0, rt_leds; - u8 debug_switch, gp_button; - - signature = inb(nirtfeatures->io_base + NIRTF_SIGNATURE); - year = inb(nirtfeatures->io_base + NIRTF_YEAR); - month = inb(nirtfeatures->io_base + NIRTF_MONTH); - day = inb(nirtfeatures->io_base + NIRTF_DAY); - hour = inb(nirtfeatures->io_base + NIRTF_HOUR); - minute = inb(nirtfeatures->io_base + NIRTF_MINUTE); - scratch = inb(nirtfeatures->io_base + NIRTF_SCRATCH); - bpinfo = inb(nirtfeatures->io_base + NIRTF_BPINFO); - railstatus1 = inb(nirtfeatures->io_base + NIRTF_RAIL_STATUS1); - railstatus2 = inb(nirtfeatures->io_base + NIRTF_RAIL_STATUS2); - lock = inb(nirtfeatures->io_base + NIRTF_LOCK); - reset = inb(nirtfeatures->io_base + NIRTF_RESET); - reset_source = inb(nirtfeatures->io_base + NIRTF_RESET_SOURCE); - processor_mode = inb(nirtfeatures->io_base + NIRTF_PROCESSOR_MODE); - system_leds = inb(nirtfeatures->io_base + NIRTF_SYSTEM_LEDS); - status_led_shift1 = - inb(nirtfeatures->io_base + NIRTF_STATUS_LED_SHIFT1); - status_led_shift0 = - inb(nirtfeatures->io_base + NIRTF_STATUS_LED_SHIFT0); - rt_leds = inb(nirtfeatures->io_base + NIRTF_RT_LEDS); - debug_switch = inb(nirtfeatures->io_base + NIRTF_DEBUG_SWITCH); - gp_button = inb(nirtfeatures->io_base + NIRTF_GP_BUTTON); - - return sprintf(buf, - "Signature: 0x%02X\n" - "Year: 0x%02X\n" - "Month: 0x%02X\n" - "Day: 0x%02X\n" - "Hour: 0x%02X\n" - "Minute: 0x%02X\n" - "Scratch: 0x%02X\n" - "BPInfo: 0x%02X\n" - "Rail status 1: 0x%02X\n" - "Rail status 2: 0x%02X\n" - "Lock: 0x%02X\n" - "Reset: 0x%02X\n" - "Reset source: 0x%02X\n" - "Processor mode: 0x%02X\n" - "System LEDs: 0x%02X\n" - "Status LED shift 1: 0x%02X\n" - "Status LED shift 0: 0x%02X\n" - "RT LEDs: 0x%02X\n" - "Debug switch: 0x%02X\n" - "GP button: 0x%02X\n", - signature, year, month, day, hour, minute, scratch, - bpinfo, railstatus1, railstatus2, lock, reset, - reset_source, processor_mode, system_leds, - status_led_shift1, status_led_shift0, rt_leds, - debug_switch, gp_button); -} - -static DEVICE_ATTR(register_dump, S_IRUGO, nirtfeatures_register_dump_get, - NULL); - static const struct attribute *nirtfeatures_attrs[] = { &dev_attr_revision.attr, &dev_attr_scratch.attr, &dev_attr_backplane_id.attr, - &dev_attr_railstatus1.attr, - &dev_attr_railstatus2.attr, - &dev_attr_reset.attr, &dev_attr_reset_source.attr, &dev_attr_no_fpga_sw.attr, &dev_attr_soft_reset.attr, @@ -466,7 +334,6 @@ static const struct attribute *nirtfeatures_attrs[] = { &dev_attr_console_out.attr, &dev_attr_ip_reset.attr, &dev_attr_safe_mode.attr, - &dev_attr_register_dump.attr, NULL }; @@ -725,21 +592,21 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) return -EBUSY; } - bpinfo = inb(nirtfeatures->io_base + NIRTF_BPINFO); + bpinfo = inb(nirtfeatures->io_base + NIRTF_PLATFORM_MISC); - bpinfo &= NIRTF_BPINFO_ID_MASK; + bpinfo &= NIRTF_PLATFORM_MISC_ID_MASK; switch (bpinfo) { - case NIRTF_BPINFO_ID_MANHATTAN: + case NIRTF_PLATFORM_MISC_ID_MANHATTAN: nirtfeatures->bpstring = "Manhattan"; break; - case NIRTF_BPINFO_ID_HAMMERHEAD: + case NIRTF_PLATFORM_MISC_ID_HAMMERHEAD: nirtfeatures->bpstring = "Hammerhead"; nirtfeatures->extra_leds = nirtfeatures_leds_cdaq; nirtfeatures->num_extra_leds = ARRAY_SIZE(nirtfeatures_leds_cdaq); break; - case NIRTF_BPINFO_ID_WINGHEAD: + case NIRTF_PLATFORM_MISC_ID_WINGHEAD: nirtfeatures->bpstring = "Winghead"; nirtfeatures->extra_leds = nirtfeatures_leds_cdaq; nirtfeatures->num_extra_leds = From c8135f9deac100d9de1b76ec51316054073b38ec Mon Sep 17 00:00:00 2001 From: Gratian Crisan Date: Tue, 13 May 2014 16:12:02 -0500 Subject: [PATCH 19/84] nirtfeatures: Physical interface element support These changes add support for PIEs (physical interface elements), which are defined as physical elements fixed to a controller/chassis with which a user can interact (e.g. LEDs and switches) and whose meaning is user-defined and implementation-specific. The support for these elements, in terms of enumerating and interacting with them (i.e. retrieving the list of elements, getting/setting their current state, enabling notifications, etc.) is embedded within the BIOS as a set of ACPI methods. The changes to the CPLD driver act as a bridge between these methods and existing Linux kernel facilities as described below to expose the elements and any applicable metadata to user mode. The metadata or knowledge needed for the interpretation thereof is not a prerequisite to interacting with the elements--it is there for upper-level value add software to use to improve the user experience. In other words, Linux users familiar with the class drivers by which the elements are surfaced should not have any issues interfacing with them without knowing the meaning of the attached metadata. Output elements, which consist currently of LEDs, are surfaced via the LED class driver. Each LED and color becomes its own LED class device with the naming convention 'nilrt:{name}:{color}'. Any additional attributes/metadata intended for upper-level software are appended to the name, each separated by colons, as suggested by the LED class driver documentation in the Linux kernel proper, except where there is already a standard way to communicate a specific piece of metadata (e.g., maximum brightness, which is exposed via the /sys/class/leds/.../ max_brightness attribute node). Input elements as surfaced via the input class driver. As with output elements, each input element registers its own separate driver whose name and associated metadata are transmitted via the name attribute attached to the input device, retrievable via the EVIOCGNAME ioctl, using the same convention as described above for output elements. The input class driver model is that events are pushed (i.e. reported) to indicate state changes, so to facilitate this, the CPLD driver has an ACPI notify callback that is invoked when an input element changes state and its BIOS support generates a general purpose event per the ACPI GPE model. The notify callback checks the instantaneous state of the input element and reports a keyboard event on its particular device with a scan code of 256 (BTN_0), where a key down event means that the input element is in the '1' state (down, engaged, on, pressed, etc.) and a key up event means that the input element is in the '0' state (off, disengaged, released, etc.). User mode software can then monitor for these specific events to determine when the state of the element has changed, or can use the EVIOCGKEY ioctl on the appropriate input device to retrieve the instantaneous state of the element. Signed-off-by: Aaron Rossetto (joshc: fixed up strnicmp -> strncasecmp for 4.0) Signed-off-by: Josh Cartwright --- drivers/misc/nirtfeatures.c | 812 +++++++++++++++++++++++++++++++++--- 1 file changed, 751 insertions(+), 61 deletions(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 51d7bdfac67cf..cce1c53d6252f 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -15,7 +15,9 @@ #include #include #include +#include #include +#include #define MODULE_NAME "nirtfeatures" @@ -57,10 +59,49 @@ #define NIRTF_SYSTEM_LEDS_POWER_GREEN 0x02 #define NIRTF_SYSTEM_LEDS_POWER_YELLOW 0x01 -#define NIRTF_RT_LEDS_USER2_GREEN 0x08 -#define NIRTF_RT_LEDS_USER2_YELLOW 0x04 -#define NIRTF_RT_LEDS_USER1_GREEN 0x02 -#define NIRTF_RT_LEDS_USER1_YELLOW 0x01 +/*===================================================================== + * ACPI NI physical interface element support + *===================================================================*/ +#define MAX_NAMELEN 64 +#define MAX_NODELEN 128 +#define MIN_PIE_CAPS_VERSION 2 +#define MAX_PIE_CAPS_VERSION 2 + +enum nirtfeatures_pie_class { + PIE_CLASS_INPUT = 0, + PIE_CLASS_OUTPUT = 1 +}; + +enum nirtfeatures_pie_type { + PIE_TYPE_UNKNOWN = 0, + PIE_TYPE_SWITCH = 1, + PIE_TYPE_LED = 2 +}; + +struct nirtfeatures_pie_descriptor { + char name[MAX_NAMELEN]; + enum nirtfeatures_pie_class pie_class; + enum nirtfeatures_pie_type pie_type; + bool is_user_visible; + unsigned int notification_value; +}; + +struct nirtfeatures_pie_descriptor_led_color { + char name[MAX_NAMELEN]; + int brightness_range_low; + int brightness_range_high; +}; + +struct nirtfeatures_pie_descriptor_switch { + unsigned int num_states; + unsigned int state_value[1]; +}; + +struct nirtfeatures_pie_location { + unsigned int element; + unsigned int subelement; +}; + /* Structures */ @@ -71,18 +112,31 @@ struct nirtfeatures { spinlock_t lock; u8 revision[5]; const char *bpstring; - struct nirtfeatures_led *extra_leds; - unsigned num_extra_leds; }; struct nirtfeatures_led { struct led_classdev cdev; struct nirtfeatures *nirtfeatures; + struct nirtfeatures_pie_location pie_location; + char name_string[MAX_NODELEN]; u8 address; u8 mask; u8 pattern_hi_addr; u8 pattern_lo_addr; + struct list_head node; +}; +LIST_HEAD(nirtfeatures_led_pie_list); + +struct nirtfeatures_switch { + struct input_dev *cdev; + struct nirtfeatures *nirtfeatures; + struct nirtfeatures_pie_descriptor pie_descriptor; + struct nirtfeatures_pie_location pie_location; + char name_string[MAX_NODELEN]; + char phys_location_string[MAX_NODELEN]; + struct list_head node; }; +LIST_HEAD(nirtfeatures_switch_pie_list); /* sysfs files */ @@ -386,20 +440,6 @@ nirtfeatures_led_brightness_get(struct led_classdev *led_cdev) } static struct nirtfeatures_led nirtfeatures_leds_common[] = { - { - { - .name = CONFIG_NI_LED_PREFIX ":user1:green", - }, - .address = NIRTF_RT_LEDS, - .mask = NIRTF_RT_LEDS_USER1_GREEN, - }, - { - { - .name = CONFIG_NI_LED_PREFIX ":user1:yellow", - }, - .address = NIRTF_RT_LEDS, - .mask = NIRTF_RT_LEDS_USER1_YELLOW, - }, { { .name = CONFIG_NI_LED_PREFIX ":status:red", @@ -433,22 +473,610 @@ static struct nirtfeatures_led nirtfeatures_leds_common[] = { }, }; -static struct nirtfeatures_led nirtfeatures_leds_cdaq[] = { - { - { - .name = CONFIG_NI_LED_PREFIX ":user2:green", - }, - .address = NIRTF_RT_LEDS, - .mask = NIRTF_RT_LEDS_USER2_GREEN, - }, - { - { - .name = CONFIG_NI_LED_PREFIX ":user2:yellow", - }, - .address = NIRTF_RT_LEDS, - .mask = NIRTF_RT_LEDS_USER2_YELLOW, - }, -}; +/*===================================================================== + * ACPI NI physical interface element support + *===================================================================*/ + +/* Note that callers of this function are responsible for deallocating + * the buffer allocated by acpi_evaluate_object() by calling + * kfree() on the pointer passed back in result_buffer. + */ +static int nirtfeatures_call_acpi_method(struct nirtfeatures *nirtfeatures, + const char *method_name, + int argc, + union acpi_object *argv, + acpi_size *result_size, + void **result_buffer) +{ + acpi_status acpi_ret; + acpi_handle acpi_hdl; + struct acpi_object_list acpi_params; + struct acpi_buffer acpi_result = { ACPI_ALLOCATE_BUFFER, NULL }; + + if (NULL == nirtfeatures || NULL == result_size || + NULL == result_buffer) + return -EINVAL; + + acpi_ret = acpi_get_handle(nirtfeatures->acpi_device->handle, + (acpi_string) method_name, &acpi_hdl); + if (ACPI_FAILURE(acpi_ret)) { + dev_err(&nirtfeatures->acpi_device->dev, + "nirtfeatures: ACPI get handle for %s failed (%d)\n", + method_name, acpi_ret); + return -1; + } + + acpi_params.count = argc; + acpi_params.pointer = argv; + + acpi_ret = acpi_evaluate_object(acpi_hdl, NULL, + &acpi_params, &acpi_result); + if (ACPI_FAILURE(acpi_ret)) { + dev_err(&nirtfeatures->acpi_device->dev, + "nirtfeatures: ACPI evaluate for %s failed (%d)\n", + method_name, acpi_ret); + return -1; + } + + *result_size = acpi_result.length; + *result_buffer = acpi_result.pointer; + return 0; +} + +/* This is the generic PIE set state wrapper. It invokes the PIES + * ACPI method to modify the state of the given PIE. + */ +static int nirtfeatures_pie_set_state(struct nirtfeatures *nirtfeatures, + unsigned int element, unsigned int subelement, int state) +{ + union acpi_object pies_args[3]; + acpi_size result_size; + void *result_buffer; + union acpi_object *acpi_buffer; + int err = 0; + + if (NULL == nirtfeatures) + return -EINVAL; + + pies_args[0].type = ACPI_TYPE_INTEGER; + pies_args[0].integer.value = element; + pies_args[1].type = ACPI_TYPE_INTEGER; + pies_args[1].integer.value = subelement; + pies_args[2].type = ACPI_TYPE_INTEGER; + pies_args[2].integer.value = state; + + /* evaluate PIES(element, subelement, value) ACPI method */ + err = nirtfeatures_call_acpi_method(nirtfeatures, "PIES", + 3, &pies_args[0], &result_size, &result_buffer); + + if (err == 0) { + acpi_buffer = (union acpi_object *) result_buffer; + if (ACPI_TYPE_INTEGER == acpi_buffer->type) + err = (int) acpi_buffer->integer.value; + kfree(result_buffer); + } + + return err; +} + +/* This is the generic PIE get state wrapper. It invokes the PIEG + * ACPI method to query the state of the given PIE. + */ +static int nirtfeatures_pie_get_state(struct nirtfeatures *nirtfeatures, + unsigned int element, unsigned int subelement, int *result) +{ + union acpi_object pies_args[2]; + acpi_size result_size; + void *result_buffer; + union acpi_object *acpi_buffer; + int err = 0; + + if (NULL == nirtfeatures || NULL == result) + return -EINVAL; + + pies_args[0].type = ACPI_TYPE_INTEGER; + pies_args[0].integer.value = element; + pies_args[1].type = ACPI_TYPE_INTEGER; + pies_args[1].integer.value = subelement; + + /* evaluate PIEG(element, subelement) ACPI method */ + err = nirtfeatures_call_acpi_method(nirtfeatures, "PIEG", + 2, &pies_args[0], &result_size, &result_buffer); + + if (err == 0) { + acpi_buffer = (union acpi_object *) result_buffer; + if (ACPI_TYPE_INTEGER == acpi_buffer->type) + *result = (int) acpi_buffer->integer.value; + kfree(result_buffer); + } + + return err; +} + +/* This function enables or disables notifications for a particular + * input class PIE. + */ +static int nirtfeatures_pie_enable_notifications( + struct nirtfeatures *nirtfeatures, + unsigned int element, unsigned int subelement, int enable) +{ + union acpi_object pies_args[3]; + acpi_size result_size; + void *result_buffer; + union acpi_object *acpi_buffer; + int err = 0; + + if (NULL == nirtfeatures) + return -EINVAL; + + pies_args[0].type = ACPI_TYPE_INTEGER; + pies_args[0].integer.value = element; + pies_args[1].type = ACPI_TYPE_INTEGER; + pies_args[1].integer.value = subelement; + pies_args[2].type = ACPI_TYPE_INTEGER; + pies_args[2].integer.value = enable; + + /* evaluate PIEF(element, subelement, enable) ACPI method */ + err = nirtfeatures_call_acpi_method(nirtfeatures, "PIEF", + 3, &pies_args[0], &result_size, &result_buffer); + + if (err == 0) { + acpi_buffer = (union acpi_object *) result_buffer; + if (ACPI_TYPE_INTEGER == acpi_buffer->type) + err = (int) acpi_buffer->integer.value; + kfree(result_buffer); + } + + return err; +} + +/* This is the set_brightness callback for a PIE-enumerated LED. + */ +static void nirtfeatures_led_pie_brightness_set( + struct led_classdev *led_cdev, enum led_brightness brightness) +{ + struct nirtfeatures_led *led = (struct nirtfeatures_led *)led_cdev; + + spin_lock(&led->nirtfeatures->lock); + + /* Delegate the control of the PIE to the ACPI method. */ + if (nirtfeatures_pie_set_state(led->nirtfeatures, + led->pie_location.element, led->pie_location.subelement, + brightness)) { + dev_err(&led->nirtfeatures->acpi_device->dev, + "nirtfeatures: set brightness failed for %s\n", + led->name_string); + } + + spin_unlock(&led->nirtfeatures->lock); +} + +/* This is the get_brightness callback for a PIE-enumerated LED. + */ +static enum led_brightness nirtfeatures_led_pie_brightness_get( + struct led_classdev *led_cdev) +{ + struct nirtfeatures_led *led = (struct nirtfeatures_led *)led_cdev; + int state = 0; + + spin_lock(&led->nirtfeatures->lock); + + if (nirtfeatures_pie_get_state(led->nirtfeatures, + led->pie_location.element, led->pie_location.subelement, &state)) { + dev_err(&led->nirtfeatures->acpi_device->dev, + "nirtfeatures: get brightness failed for %s\n", + led->name_string); + } + + spin_unlock(&led->nirtfeatures->lock); + return state; +} + +/* Parse a PIE LED color caps package and populate the + * corresponding nirtfeatures_pie_descriptor_led_color structure. + */ +static int nirtfeatures_parse_led_pie_color(struct nirtfeatures *nirtfeatures, + unsigned int pie_caps_version, + struct nirtfeatures_pie_descriptor_led_color *led_color_descriptor, + union acpi_object *acpi_buffer) +{ + unsigned int i; + + if (NULL == nirtfeatures || NULL == led_color_descriptor || + NULL == acpi_buffer) + return -EINVAL; + + /* element 0 of a PIE LED color caps package is the name */ + if (ACPI_TYPE_BUFFER == acpi_buffer->package.elements[0].type) { + for (i = 0; + i < acpi_buffer->package.elements[0].buffer.length; i++) { + /* get pointer to Nth Unicode character in name */ + unsigned short *unicode_char = (unsigned short *) + (acpi_buffer->package.elements[0].buffer.pointer + + (2 * i)); + /* naive convert to ASCII */ + led_color_descriptor->name[i] = + (char) *unicode_char & 0xff; + } + } else + return -EINVAL; + + /* element 1 is the brightness min value */ + if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[1].type) + led_color_descriptor->brightness_range_low = + (int) acpi_buffer->package.elements[1].integer.value; + else + return -EINVAL; + + /* element 2 is the brightness max value */ + if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[2].type) + led_color_descriptor->brightness_range_high = + (int) acpi_buffer->package.elements[2].integer.value; + else + return -EINVAL; + + return 0; +} + +/* Parse a PIE LED caps package and create an LED class device + * with the appropriate metadata. + */ +static int nirtfeatures_parse_led_pie( + struct nirtfeatures *nirtfeatures, + unsigned int pie_caps_version, + unsigned int pie_element, + struct nirtfeatures_pie_descriptor *pie, + union acpi_object *acpi_buffer) +{ + unsigned int num_colors; + unsigned int i; + struct nirtfeatures_pie_descriptor_led_color led_descriptor; + struct nirtfeatures_led *led_dev; + int err; + + if (NULL == nirtfeatures || NULL == pie || + NULL == acpi_buffer) + return -EINVAL; + + if (ACPI_TYPE_PACKAGE != acpi_buffer->type) + return -EINVAL; + + /* element 0 is the number of colors */ + if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[0].type) { + num_colors = (unsigned int) + acpi_buffer->package.elements[0].integer.value; + } else { + return -EINVAL; + } + + /* parse color caps and create LED class device */ + for (i = 0; i < num_colors; i++) { + if (nirtfeatures_parse_led_pie_color(nirtfeatures, + pie_caps_version, &led_descriptor, + &(acpi_buffer->package.elements[i + 1]))) + return -EINVAL; + + /* create an LED class device for this LED */ + led_dev = kzalloc(sizeof(struct nirtfeatures_led), GFP_KERNEL); + if (NULL == led_dev) + return -ENOMEM; + + /* for compatibility with existing LVRT support, PIEs beginning + * with 'user' should not affix the uservisible attribute to + * their name */ + if (strncasecmp(pie->name, "user", strlen("user")) != 0) { + snprintf(led_dev->name_string, MAX_NODELEN, + "%s:%s:%s:uservisible=%d", + CONFIG_NI_LED_PREFIX, + pie->name, led_descriptor.name, + pie->is_user_visible); + } else { + snprintf(led_dev->name_string, MAX_NODELEN, + "%s:%s:%s", + CONFIG_NI_LED_PREFIX, + pie->name, led_descriptor.name); + } + + led_dev->cdev.name = led_dev->name_string; + led_dev->cdev.brightness = + led_descriptor.brightness_range_low; + led_dev->cdev.max_brightness = + led_descriptor.brightness_range_high; + led_dev->cdev.brightness_set = + nirtfeatures_led_pie_brightness_set; + led_dev->cdev.brightness_get = + nirtfeatures_led_pie_brightness_get; + led_dev->nirtfeatures = nirtfeatures; + led_dev->pie_location.element = pie_element; + led_dev->pie_location.subelement = i; + + err = led_classdev_register(&nirtfeatures->acpi_device->dev, + &led_dev->cdev); + if (0 != err) { + kfree(led_dev); + return err; + } + + list_add_tail(&led_dev->node, &nirtfeatures_led_pie_list); + } + + return 0; +} + +/* Parse a PIE switch caps package and create an input class device + * with the appropriate metadata. + */ +static int nirtfeatures_parse_switch_pie(struct nirtfeatures *nirtfeatures, + unsigned int pie_caps_version, + unsigned int pie_element, + struct nirtfeatures_pie_descriptor *pie, + union acpi_object *acpi_buffer) +{ + unsigned int num_states; + unsigned int i; + struct nirtfeatures_pie_descriptor_switch *switch_descriptor = NULL; + struct nirtfeatures_switch *switch_dev = NULL; + int err = 0; + + if (NULL == nirtfeatures || NULL == pie || NULL == acpi_buffer) + return -EINVAL; + + if (ACPI_TYPE_PACKAGE != acpi_buffer->type) + return -EINVAL; + + /* element 0 is the number of states */ + if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[0].type) + num_states = (unsigned int) + acpi_buffer->package.elements[0].integer.value; + else + return -EINVAL; + + /* allocate storage for switch descriptor */ + switch_descriptor = kzalloc( + sizeof(struct nirtfeatures_pie_descriptor_switch) + + sizeof(int) * (num_states - 1), GFP_KERNEL); + if (NULL == switch_descriptor) + return -ENOMEM; + + switch_descriptor->num_states = num_states; + + /* parse individual states in elements 1..N-1 */ + for (i = 0; i < num_states; i++) { + if (ACPI_TYPE_INTEGER != + acpi_buffer->package.elements[i + 1].type) { + err = -EINVAL; + goto exit; + } + + switch_descriptor->state_value[i] = + (int) acpi_buffer->package.elements[i + 1].integer.value; + } + + /* create an input class device for this switch */ + switch_dev = kzalloc(sizeof(struct nirtfeatures_switch), GFP_KERNEL); + if (NULL == switch_dev) { + err = -ENOMEM; + goto exit; + } + + switch_dev->cdev = input_allocate_device(); + if (NULL == switch_dev->cdev) { + err = -ENOMEM; + goto exit_dealloc_switch_dev; + } + + switch_dev->nirtfeatures = nirtfeatures; + switch_dev->pie_location.element = pie_element; + switch_dev->pie_location.subelement = 0; + memcpy(&switch_dev->pie_descriptor, pie, + sizeof(struct nirtfeatures_pie_descriptor)); + + snprintf(switch_dev->name_string, MAX_NODELEN, + "%s:%s:uservisible=%d:states=(", + CONFIG_NI_LED_PREFIX, pie->name, pie->is_user_visible); + for (i = 0; i < switch_descriptor->num_states; i++) { + char temp[4] = { '\0' }; + + sprintf(temp, "%d%c", switch_descriptor->state_value[i], + (i < switch_descriptor->num_states - 1) ? ',' : ')'); + strncat(switch_dev->name_string, temp, MAX_NODELEN); + } + + snprintf(switch_dev->phys_location_string, MAX_NODELEN, "%s/%s/%s", + CONFIG_NI_LED_PREFIX, nirtfeatures->bpstring, pie->name); + + switch_dev->cdev->name = switch_dev->name_string; + switch_dev->cdev->phys = switch_dev->phys_location_string; + switch_dev->cdev->id.bustype = BUS_HOST; + switch_dev->cdev->id.vendor = 0x3923; + switch_dev->cdev->id.product = pie->pie_type; + switch_dev->cdev->id.version = pie_caps_version; + switch_dev->cdev->dev.parent = &nirtfeatures->acpi_device->dev; + + switch_dev->cdev->evbit[0] = BIT_MASK(EV_KEY); + set_bit(BTN_0, switch_dev->cdev->keybit); + + err = input_register_device(switch_dev->cdev); + if (0 != err) { + input_free_device(switch_dev->cdev); + goto exit_dealloc_switch_dev; + } + + /* if this PIE supports notifications, enable them now */ + if (pie->notification_value != 0) { + err = nirtfeatures_pie_enable_notifications(nirtfeatures, + pie_element, 0, 1); + if (0 != err) { + input_unregister_device(switch_dev->cdev); + input_free_device(switch_dev->cdev); + goto exit_dealloc_switch_dev; + } + } + + /* add the new device to our list of switch PIEs */ + list_add_tail(&switch_dev->node, &nirtfeatures_switch_pie_list); + goto exit; + +exit_dealloc_switch_dev: + kfree(switch_dev); + +exit: + kfree(switch_descriptor); + return err; +} + + +/* Parse a single PIE caps package from the PIEC buffer, determine the + * type of PIE it is, then dispatch to the appropriate parsing routine. + */ +static int nirtfeatures_parse_one_pie(struct nirtfeatures *nirtfeatures, + unsigned int pie_caps_version, + unsigned int pie_element, + union acpi_object *acpi_buffer) +{ + struct nirtfeatures_pie_descriptor pie; + unsigned int i; + + if (NULL == nirtfeatures || NULL == acpi_buffer) + return -EINVAL; + + /* check for proper type and number of elements */ + if (ACPI_TYPE_PACKAGE != acpi_buffer->type || + 6 != acpi_buffer->package.count) + return -EINVAL; + + /* element 0 of the package is the name */ + if (ACPI_TYPE_BUFFER == acpi_buffer->package.elements[0].type) { + for (i = 0; + i < acpi_buffer->package.elements[0].buffer.length && + i < MAX_NAMELEN; i++) { + /* get pointer to Nth Unicode character in name */ + unsigned short *unicode_char = (unsigned short *) + (acpi_buffer->package.elements[0].buffer.pointer + + (2 * i)); + /* naive convert to ASCII */ + pie.name[i] = (char) *unicode_char & 0xff; + } + } else + return -EINVAL; + + /* element 1 of the package is the PIE class */ + if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[1].type) + pie.pie_class = (enum nirtfeatures_pie_class) + acpi_buffer->package.elements[1].integer.value; + else + return -EINVAL; + + /* element 2 of the package is the PIE type */ + if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[2].type) + pie.pie_type = (enum nirtfeatures_pie_type) + acpi_buffer->package.elements[2].integer.value; + else + return -EINVAL; + + /* element 4 of an package is the visible flag */ + if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[4].type) + pie.is_user_visible = + (acpi_buffer->package.elements[4].integer.value != 0); + else + return -EINVAL; + + /* element 5 of the package is the notification value */ + if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[5].type) + pie.notification_value = + acpi_buffer->package.elements[5].integer.value; + else + return -EINVAL; + + /* parse the type-specific descriptor in element 3 */ + switch (pie.pie_type) { + case PIE_TYPE_LED: + if (nirtfeatures_parse_led_pie(nirtfeatures, + pie_caps_version, pie_element, &pie, + &(acpi_buffer->package.elements[3]))) + return -EINVAL; + break; + case PIE_TYPE_SWITCH: + if (nirtfeatures_parse_switch_pie(nirtfeatures, + pie_caps_version, pie_element, &pie, + &(acpi_buffer->package.elements[3]))) + return -EINVAL; + break; + + default: + return -EINVAL; + break; + } + + return 0; +} + +/* Populate the list of physical interface elements from the table in + * the DSDT and then generate the appropriate class devices. + */ +static int nirtfeatures_populate_pies(struct nirtfeatures *nirtfeatures) +{ + acpi_size result_size; + void *result_buffer; + union acpi_object *acpi_buffer; + unsigned int num_elements = 0; + unsigned int pie_caps_version; + unsigned int i; + unsigned int err = 0; + + if (NULL == nirtfeatures) + return -EINVAL; + + /* get the PIE descriptor buffer from DSDT */ + if (nirtfeatures_call_acpi_method(nirtfeatures, + "PIEC", 0, NULL, &result_size, &result_buffer)) + return -1; + + acpi_buffer = (union acpi_object *) result_buffer; + if (ACPI_TYPE_PACKAGE != acpi_buffer->type) { + err = -1; + goto exit; + } + + /* the first element of the package is the caps version */ + if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[0].type) + pie_caps_version = (unsigned int) + acpi_buffer->package.elements[0].integer.value; + else { + err = -1; + goto exit; + } + + if (pie_caps_version < MIN_PIE_CAPS_VERSION || + pie_caps_version > MAX_PIE_CAPS_VERSION) { + dev_err(&nirtfeatures->acpi_device->dev, + "nirtfeatures: invalid PIE caps version\n"); + err = -1; + goto exit; + } + + /* the second element of the package is the number of PIEs */ + if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[1].type) + num_elements = (unsigned int) + acpi_buffer->package.elements[1].integer.value; + else { + err = -1; + goto exit; + } + + /* parse elements 2..N as PIE descriptors */ + for (i = 2; i < acpi_buffer->package.count; i++) { + err = nirtfeatures_parse_one_pie(nirtfeatures, + pie_caps_version, i - 2, + &(acpi_buffer->package.elements[i])); + if (0 != err) + break; + } + +exit: + kfree(result_buffer); + return err; +} static int nirtfeatures_create_leds(struct nirtfeatures *nirtfeatures) { @@ -474,37 +1102,57 @@ static int nirtfeatures_create_leds(struct nirtfeatures *nirtfeatures) return err; } - for (i = 0; i < nirtfeatures->num_extra_leds; ++i) { + return 0; +} - nirtfeatures->extra_leds[i].nirtfeatures = nirtfeatures; +static void nirtfeatures_remove_leds(struct nirtfeatures *nirtfeatures) +{ + int i; - if (0 == nirtfeatures->extra_leds[i].cdev.max_brightness) - nirtfeatures->extra_leds[i].cdev.max_brightness = 1; + for (i = 0; i < ARRAY_SIZE(nirtfeatures_leds_common); ++i) + led_classdev_unregister(&nirtfeatures_leds_common[i].cdev); +} - nirtfeatures->extra_leds[i].cdev.brightness_set = - nirtfeatures_led_brightness_set; +static void nirtfeatures_remove_led_pies(struct nirtfeatures *nirtfeatures) +{ + struct nirtfeatures_led *cdev_iter; + struct nirtfeatures_led *temp; - nirtfeatures->extra_leds[i].cdev.brightness_get = - nirtfeatures_led_brightness_get; + spin_lock(&nirtfeatures->lock); - err = led_classdev_register(&nirtfeatures->acpi_device->dev, - &nirtfeatures->extra_leds[i].cdev); - if (err) - return err; + /* walk the list of non-fixed LEDs and unregister/free their devices */ + list_for_each_entry_safe( + cdev_iter, temp, &nirtfeatures_led_pie_list, node) { + led_classdev_unregister(&cdev_iter->cdev); + kfree(cdev_iter); } - return 0; + spin_unlock(&nirtfeatures->lock); } -static void nirtfeatures_remove_leds(struct nirtfeatures *nirtfeatures) +static void nirtfeatures_remove_switch_pies(struct nirtfeatures *nirtfeatures) { - int i; + struct nirtfeatures_switch *cdev_iter; + struct nirtfeatures_switch *temp; - for (i = 0; i < ARRAY_SIZE(nirtfeatures_leds_common); ++i) - led_classdev_unregister(&nirtfeatures_leds_common[i].cdev); + spin_lock(&nirtfeatures->lock); + + /* walk the list of switch devices and unregister/free each one */ + list_for_each_entry_safe( + cdev_iter, temp, &nirtfeatures_switch_pie_list, node) { + /* disable notifications for this PIE if supported */ + if (cdev_iter->pie_descriptor.notification_value != 0) { + nirtfeatures_pie_enable_notifications(nirtfeatures, + cdev_iter->pie_location.element, + cdev_iter->pie_location.subelement, + 0); + } + input_unregister_device(cdev_iter->cdev); + input_free_device(cdev_iter->cdev); + kfree(cdev_iter); + } - for (i = 0; i < nirtfeatures->num_extra_leds; ++i) - led_classdev_unregister(&nirtfeatures->extra_leds[i].cdev); + spin_unlock(&nirtfeatures->lock); } /* ACPI driver */ @@ -540,12 +1188,53 @@ static acpi_status nirtfeatures_resources(struct acpi_resource *res, void *data) return AE_OK; } +/* Process a notification from ACPI, which typically occurs when a switch + * PIE is signalling a change of state via its GPE. + */ +static void nirtfeatures_acpi_notify(struct acpi_device *device, u32 event) +{ + /* find the switch PIE for which this notification was generated, + * and push an event into its associated input subsystem node + */ + struct nirtfeatures_switch *iter; + int state = 0; + struct nirtfeatures *nirtfeatures = + (struct nirtfeatures *)device->driver_data; + + spin_lock(&nirtfeatures->lock); + + list_for_each_entry(iter, &nirtfeatures_switch_pie_list, node) { + if (event == iter->pie_descriptor.notification_value) { + + /* query instantaneous switch state */ + if (!nirtfeatures_pie_get_state(iter->nirtfeatures, + iter->pie_location.element, + iter->pie_location.subelement, + &state)) { + /* push current state of switch */ + input_report_key(iter->cdev, BTN_0, !!state); + input_sync(iter->cdev); + } + spin_unlock(&nirtfeatures->lock); + return; + } + } + + spin_unlock(&nirtfeatures->lock); + + dev_err(&device->dev, "no input found for notification (event %02X)\n", + event); +} + static int nirtfeatures_acpi_remove(struct acpi_device *device) { struct nirtfeatures *nirtfeatures = device->driver_data; nirtfeatures_remove_leds(nirtfeatures); + nirtfeatures_remove_led_pies(nirtfeatures); + nirtfeatures_remove_switch_pies(nirtfeatures); + sysfs_remove_files(&nirtfeatures->acpi_device->dev.kobj, nirtfeatures_attrs); @@ -602,15 +1291,9 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) break; case NIRTF_PLATFORM_MISC_ID_HAMMERHEAD: nirtfeatures->bpstring = "Hammerhead"; - nirtfeatures->extra_leds = nirtfeatures_leds_cdaq; - nirtfeatures->num_extra_leds = - ARRAY_SIZE(nirtfeatures_leds_cdaq); break; case NIRTF_PLATFORM_MISC_ID_WINGHEAD: nirtfeatures->bpstring = "Winghead"; - nirtfeatures->extra_leds = nirtfeatures_leds_cdaq; - nirtfeatures->num_extra_leds = - ARRAY_SIZE(nirtfeatures_leds_cdaq); break; default: dev_err(&nirtfeatures->acpi_device->dev, @@ -622,6 +1305,12 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) spin_lock_init(&nirtfeatures->lock); + err = nirtfeatures_populate_pies(nirtfeatures); + if (0 != err) { + nirtfeatures_acpi_remove(device); + return err; + } + nirtfeatures->revision[0] = inb(nirtfeatures->io_base + NIRTF_YEAR); nirtfeatures->revision[1] = inb(nirtfeatures->io_base + NIRTF_MONTH); nirtfeatures->revision[2] = inb(nirtfeatures->io_base + NIRTF_DAY); @@ -660,6 +1349,7 @@ static struct acpi_driver nirtfeatures_acpi_driver = { .ops = { .add = nirtfeatures_acpi_add, .remove = nirtfeatures_acpi_remove, + .notify = nirtfeatures_acpi_notify, }, }; From d5b3677b2a7968492bd494a3832c75edd4a9a49c Mon Sep 17 00:00:00 2001 From: Nathan Sullivan Date: Tue, 29 Sep 2015 12:45:12 -0500 Subject: [PATCH 20/84] nirtfeatures: Don't rename wifi LEDs For compatibility with myRIO, don't change the name of the wireless PIE LEDs. Signed-off-by: Nathan Sullivan Reviewed-by: Jaeden Amero Reviewed-by: Josh Cartwright Natinst-ReviewBoard-ID: 107067 Natinst-CAR-ID: 540272 --- drivers/misc/nirtfeatures.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index cce1c53d6252f..bc2a679c841f0 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -762,9 +762,10 @@ static int nirtfeatures_parse_led_pie( return -ENOMEM; /* for compatibility with existing LVRT support, PIEs beginning - * with 'user' should not affix the uservisible attribute to - * their name */ - if (strncasecmp(pie->name, "user", strlen("user")) != 0) { + * with 'user' or 'wifi' should not affix the uservisible + * attribute to their name */ + if (strncasecmp(pie->name, "user", strlen("user")) != 0 && + strncasecmp(pie->name, "wifi", strlen("wifi")) != 0) { snprintf(led_dev->name_string, MAX_NODELEN, "%s:%s:%s:uservisible=%d", CONFIG_NI_LED_PREFIX, From a75f704ee098f91b0af321388541e45d5a5e0d0f Mon Sep 17 00:00:00 2001 From: James Minor Date: Tue, 9 Aug 2016 13:50:02 -0500 Subject: [PATCH 21/84] nirtfeatures: Add control for WiFi reset as a vmmc regulator The MMC driver will enable/disable external regulators as part of enabling/disabling the SD Host Controller. The WLAN_PWD_L line (controlled from the CPLD) must be enabled/disabled at the same time that the controller is enabled/disabled. Allow the MMC driver to just control that pin directly via the regulator framework. Signed-off-by: James Minor --- drivers/misc/Kconfig | 1 + drivers/misc/nirtfeatures.c | 127 +++++++++++++++++++++++++++++++++++- 2 files changed, 126 insertions(+), 2 deletions(-) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index de63d1db0ad90..a878fd022b9c4 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -473,6 +473,7 @@ config HISI_HIKEY_USB config NI_RT_FEATURES bool "NI 903x/913x support" depends on X86 && ACPI + select REGULATOR help This driver exposes LEDs and other features of NI 903x/913x Real-Time controllers. diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index bc2a679c841f0..0c8587c54041d 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -18,6 +18,8 @@ #include #include #include +#include +#include #define MODULE_NAME "nirtfeatures" @@ -36,6 +38,7 @@ #define NIRTF_STATUS_LED_SHIFT1 0x21 #define NIRTF_STATUS_LED_SHIFT0 0x22 #define NIRTF_RT_LEDS 0x23 +#define NIRTF_WLAN_CONTROLREG 0x32 #define NIRTF_IO_SIZE 0x40 @@ -59,6 +62,9 @@ #define NIRTF_SYSTEM_LEDS_POWER_GREEN 0x02 #define NIRTF_SYSTEM_LEDS_POWER_YELLOW 0x01 +#define NIRTF_WLAN_RESET_N 0x02 +#define NIRTF_WLAN_RESETENABLE 0x01 + /*===================================================================== * ACPI NI physical interface element support *===================================================================*/ @@ -112,6 +118,8 @@ struct nirtfeatures { spinlock_t lock; u8 revision[5]; const char *bpstring; + bool has_wifi; + struct regulator_dev *reg_dev; }; struct nirtfeatures_led { @@ -733,6 +741,7 @@ static int nirtfeatures_parse_led_pie( struct nirtfeatures_pie_descriptor_led_color led_descriptor; struct nirtfeatures_led *led_dev; int err; + int is_wifi, is_user; if (NULL == nirtfeatures || NULL == pie || NULL == acpi_buffer) @@ -764,8 +773,10 @@ static int nirtfeatures_parse_led_pie( /* for compatibility with existing LVRT support, PIEs beginning * with 'user' or 'wifi' should not affix the uservisible * attribute to their name */ - if (strncasecmp(pie->name, "user", strlen("user")) != 0 && - strncasecmp(pie->name, "wifi", strlen("wifi")) != 0) { + is_user = strncasecmp(pie->name, "user", strlen("user")); + is_wifi = strncasecmp(pie->name, "wifi", strlen("wifi")); + if (is_user != 0 && + is_wifi != 0) { snprintf(led_dev->name_string, MAX_NODELEN, "%s:%s:%s:uservisible=%d", CONFIG_NI_LED_PREFIX, @@ -778,6 +789,10 @@ static int nirtfeatures_parse_led_pie( pie->name, led_descriptor.name); } + /* The presence of any WiFi LED means this target has wifi */ + if (is_wifi == 0) + nirtfeatures->has_wifi = true; + led_dev->cdev.name = led_dev->name_string; led_dev->cdev.brightness = led_descriptor.brightness_range_low; @@ -1239,6 +1254,9 @@ static int nirtfeatures_acpi_remove(struct acpi_device *device) sysfs_remove_files(&nirtfeatures->acpi_device->dev.kobj, nirtfeatures_attrs); + if (nirtfeatures->reg_dev) + regulator_unregister(nirtfeatures->reg_dev); + if ((nirtfeatures->io_base != 0) && (nirtfeatures->io_size == NIRTF_IO_SIZE)) release_region(nirtfeatures->io_base, nirtfeatures->io_size); @@ -1250,6 +1268,102 @@ static int nirtfeatures_acpi_remove(struct acpi_device *device) return 0; } +static int nirtfeatures_wifi_regulator_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + return 3300000; +} + +static int nirtfeatures_wifi_regulator_enable(struct regulator_dev *dev) +{ + struct nirtfeatures *nirtfeatures; + + nirtfeatures = rdev_get_drvdata(dev); + + /* WiFi out of Reset */ + outb(NIRTF_WLAN_RESET_N | NIRTF_WLAN_RESETENABLE, + nirtfeatures->io_base + NIRTF_WLAN_CONTROLREG); + /* WiFi Reset Disable */ + outb(NIRTF_WLAN_RESET_N, + nirtfeatures->io_base + NIRTF_WLAN_CONTROLREG); + + return 0; +} + +static int nirtfeatures_wifi_regulator_disable(struct regulator_dev *dev) +{ + struct nirtfeatures *nirtfeatures; + + nirtfeatures = rdev_get_drvdata(dev); + + /* WiFi Reset Enable */ + outb(NIRTF_WLAN_RESET_N | NIRTF_WLAN_RESETENABLE, + nirtfeatures->io_base + NIRTF_WLAN_CONTROLREG); + /* WiFi into Reset */ + outb(NIRTF_WLAN_RESETENABLE, + nirtfeatures->io_base + NIRTF_WLAN_CONTROLREG); + + /* Silex specs say to assert reset for 5 us, make it 10 to be sure */ + usleep_range(10, 1000); + + return 0; +} + +static int nirtfeatures_wifi_regulator_is_enabled(struct regulator_dev *dev) +{ + struct nirtfeatures *nirtfeatures; + u8 data; + + nirtfeatures = rdev_get_drvdata(dev); + + data = inb(nirtfeatures->io_base + NIRTF_WLAN_CONTROLREG); + + return !!(data & NIRTF_WLAN_RESET_N); +} + +static struct regulator_ops nirtfeatures_wifi_regulator_voltage_ops = { + .list_voltage = nirtfeatures_wifi_regulator_list_voltage, + .enable = nirtfeatures_wifi_regulator_enable, + .disable = nirtfeatures_wifi_regulator_disable, + .is_enabled = nirtfeatures_wifi_regulator_is_enabled +}; + +static const struct regulator_desc nirtfeatures_wifi_regulator_desc = { + .name = "vmmc", + .id = -1, + .n_voltages = 1, + .ops = &nirtfeatures_wifi_regulator_voltage_ops, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE +}; + +static const struct regulator_init_data wifi_reset_init_data = { + .constraints = { + .min_uV = 3300000, + .max_uV = 3300000, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + } +}; + +static int nirtfeatures_wifi_regulator_init(struct device *dev, + struct nirtfeatures *nirtfeatures) +{ + struct regulator_config cfg = { }; + struct regulator_dev *reg_dev; + + cfg.dev = dev; + cfg.init_data = &wifi_reset_init_data; + cfg.driver_data = nirtfeatures; + reg_dev = regulator_register(&nirtfeatures_wifi_regulator_desc, + &cfg); + if (IS_ERR(reg_dev)) { + pr_err("Failed to register vmmc regulator for wifi\n"); + return -ENODEV; + } + nirtfeatures->reg_dev = reg_dev; + return 0; +} + static int nirtfeatures_acpi_add(struct acpi_device *device) { struct nirtfeatures *nirtfeatures; @@ -1336,6 +1450,15 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) nirtfeatures->io_base, nirtfeatures->io_base + nirtfeatures->io_size - 1); + if (nirtfeatures->has_wifi) { + err = nirtfeatures_wifi_regulator_init(&device->dev, + nirtfeatures); + if (0 != err) { + nirtfeatures_acpi_remove(device); + return err; + } + } + return 0; } From b07295d798854f0d8d79662d2ee9e100c751e7c6 Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Fri, 9 Sep 2016 13:07:35 -0500 Subject: [PATCH 22/84] nirtfeatures: Add Fire Eagle backplane ID Add the new Fire Eagle backplane ID so we stop complaining on boot. Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: Xander Huff Natinst-ReviewBoard-ID: 152156 --- drivers/misc/nirtfeatures.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 0c8587c54041d..414ce7df12f72 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -46,6 +46,7 @@ #define NIRTF_PLATFORM_MISC_ID_MASK 0x07 #define NIRTF_PLATFORM_MISC_ID_MANHATTAN 0 +#define NIRTF_PLATFORM_MISC_ID_FIRE_EAGLE 2 #define NIRTF_PLATFORM_MISC_ID_HAMMERHEAD 4 #define NIRTF_PLATFORM_MISC_ID_WINGHEAD 5 @@ -1404,6 +1405,9 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) case NIRTF_PLATFORM_MISC_ID_MANHATTAN: nirtfeatures->bpstring = "Manhattan"; break; + case NIRTF_PLATFORM_MISC_ID_FIRE_EAGLE: + nirtfeatures->bpstring = "Fire Eagle"; + break; case NIRTF_PLATFORM_MISC_ID_HAMMERHEAD: nirtfeatures->bpstring = "Hammerhead"; break; From 66f7c6406ec54ed008b79fb9d7626acf21dd3a72 Mon Sep 17 00:00:00 2001 From: Xander Huff Date: Fri, 14 Oct 2016 13:54:00 -0500 Subject: [PATCH 23/84] nirtfeatures: Housecleaning of comments, comparisons, etc. Fix checkpatch warnings: Block comments use * on subsequent lines Block comments use a trailing */ on a separate line Comparisons should place the constant on the right side of the test break is not useful after a goto or return Signed-off-by: Xander Huff Natinst-ReviewBoard-ID: 157395 --- drivers/misc/nirtfeatures.c | 107 ++++++++++++++++++------------------ 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 414ce7df12f72..41b2c63e86c24 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -68,7 +68,8 @@ /*===================================================================== * ACPI NI physical interface element support - *===================================================================*/ + *===================================================================== + */ #define MAX_NAMELEN 64 #define MAX_NODELEN 128 #define MIN_PIE_CAPS_VERSION 2 @@ -444,7 +445,8 @@ nirtfeatures_led_brightness_get(struct led_classdev *led_cdev) data = inb(led->nirtfeatures->io_base + led->address); /* For the yellow status LED, the blink pattern used for brightness - on write is write-only, so we just return on/off for all LEDs. */ + * on write is write-only, so we just return on/off for all LEDs. + */ return (data & led->mask) ? LED_FULL : LED_OFF; } @@ -484,7 +486,8 @@ static struct nirtfeatures_led nirtfeatures_leds_common[] = { /*===================================================================== * ACPI NI physical interface element support - *===================================================================*/ + *===================================================================== + */ /* Note that callers of this function are responsible for deallocating * the buffer allocated by acpi_evaluate_object() by calling @@ -502,8 +505,8 @@ static int nirtfeatures_call_acpi_method(struct nirtfeatures *nirtfeatures, struct acpi_object_list acpi_params; struct acpi_buffer acpi_result = { ACPI_ALLOCATE_BUFFER, NULL }; - if (NULL == nirtfeatures || NULL == result_size || - NULL == result_buffer) + if (nirtfeatures == NULL || result_size == NULL || + result_buffer == NULL) return -EINVAL; acpi_ret = acpi_get_handle(nirtfeatures->acpi_device->handle, @@ -544,7 +547,7 @@ static int nirtfeatures_pie_set_state(struct nirtfeatures *nirtfeatures, union acpi_object *acpi_buffer; int err = 0; - if (NULL == nirtfeatures) + if (nirtfeatures == NULL) return -EINVAL; pies_args[0].type = ACPI_TYPE_INTEGER; @@ -560,7 +563,7 @@ static int nirtfeatures_pie_set_state(struct nirtfeatures *nirtfeatures, if (err == 0) { acpi_buffer = (union acpi_object *) result_buffer; - if (ACPI_TYPE_INTEGER == acpi_buffer->type) + if (acpi_buffer->type == ACPI_TYPE_INTEGER) err = (int) acpi_buffer->integer.value; kfree(result_buffer); } @@ -580,7 +583,7 @@ static int nirtfeatures_pie_get_state(struct nirtfeatures *nirtfeatures, union acpi_object *acpi_buffer; int err = 0; - if (NULL == nirtfeatures || NULL == result) + if (nirtfeatures == NULL || result == NULL) return -EINVAL; pies_args[0].type = ACPI_TYPE_INTEGER; @@ -594,7 +597,7 @@ static int nirtfeatures_pie_get_state(struct nirtfeatures *nirtfeatures, if (err == 0) { acpi_buffer = (union acpi_object *) result_buffer; - if (ACPI_TYPE_INTEGER == acpi_buffer->type) + if (acpi_buffer->type == ACPI_TYPE_INTEGER) *result = (int) acpi_buffer->integer.value; kfree(result_buffer); } @@ -615,7 +618,7 @@ static int nirtfeatures_pie_enable_notifications( union acpi_object *acpi_buffer; int err = 0; - if (NULL == nirtfeatures) + if (nirtfeatures == NULL) return -EINVAL; pies_args[0].type = ACPI_TYPE_INTEGER; @@ -631,7 +634,7 @@ static int nirtfeatures_pie_enable_notifications( if (err == 0) { acpi_buffer = (union acpi_object *) result_buffer; - if (ACPI_TYPE_INTEGER == acpi_buffer->type) + if (acpi_buffer->type == ACPI_TYPE_INTEGER) err = (int) acpi_buffer->integer.value; kfree(result_buffer); } @@ -691,12 +694,12 @@ static int nirtfeatures_parse_led_pie_color(struct nirtfeatures *nirtfeatures, { unsigned int i; - if (NULL == nirtfeatures || NULL == led_color_descriptor || - NULL == acpi_buffer) + if (nirtfeatures == NULL || led_color_descriptor == NULL || + acpi_buffer == NULL) return -EINVAL; /* element 0 of a PIE LED color caps package is the name */ - if (ACPI_TYPE_BUFFER == acpi_buffer->package.elements[0].type) { + if (acpi_buffer->package.elements[0].type == ACPI_TYPE_BUFFER) { for (i = 0; i < acpi_buffer->package.elements[0].buffer.length; i++) { /* get pointer to Nth Unicode character in name */ @@ -711,14 +714,14 @@ static int nirtfeatures_parse_led_pie_color(struct nirtfeatures *nirtfeatures, return -EINVAL; /* element 1 is the brightness min value */ - if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[1].type) + if (acpi_buffer->package.elements[1].type == ACPI_TYPE_INTEGER) led_color_descriptor->brightness_range_low = (int) acpi_buffer->package.elements[1].integer.value; else return -EINVAL; /* element 2 is the brightness max value */ - if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[2].type) + if (acpi_buffer->package.elements[2].type == ACPI_TYPE_INTEGER) led_color_descriptor->brightness_range_high = (int) acpi_buffer->package.elements[2].integer.value; else @@ -744,15 +747,15 @@ static int nirtfeatures_parse_led_pie( int err; int is_wifi, is_user; - if (NULL == nirtfeatures || NULL == pie || - NULL == acpi_buffer) + if (nirtfeatures == NULL || pie == NULL || + acpi_buffer == NULL) return -EINVAL; - if (ACPI_TYPE_PACKAGE != acpi_buffer->type) + if (acpi_buffer->type != ACPI_TYPE_PACKAGE) return -EINVAL; /* element 0 is the number of colors */ - if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[0].type) { + if (acpi_buffer->package.elements[0].type == ACPI_TYPE_INTEGER) { num_colors = (unsigned int) acpi_buffer->package.elements[0].integer.value; } else { @@ -768,12 +771,13 @@ static int nirtfeatures_parse_led_pie( /* create an LED class device for this LED */ led_dev = kzalloc(sizeof(struct nirtfeatures_led), GFP_KERNEL); - if (NULL == led_dev) + if (led_dev == NULL) return -ENOMEM; /* for compatibility with existing LVRT support, PIEs beginning * with 'user' or 'wifi' should not affix the uservisible - * attribute to their name */ + * attribute to their name + */ is_user = strncasecmp(pie->name, "user", strlen("user")); is_wifi = strncasecmp(pie->name, "wifi", strlen("wifi")); if (is_user != 0 && @@ -809,7 +813,7 @@ static int nirtfeatures_parse_led_pie( err = led_classdev_register(&nirtfeatures->acpi_device->dev, &led_dev->cdev); - if (0 != err) { + if (err != 0) { kfree(led_dev); return err; } @@ -835,14 +839,14 @@ static int nirtfeatures_parse_switch_pie(struct nirtfeatures *nirtfeatures, struct nirtfeatures_switch *switch_dev = NULL; int err = 0; - if (NULL == nirtfeatures || NULL == pie || NULL == acpi_buffer) + if (nirtfeatures == NULL || pie == NULL || acpi_buffer == NULL) return -EINVAL; - if (ACPI_TYPE_PACKAGE != acpi_buffer->type) + if (acpi_buffer->type != ACPI_TYPE_PACKAGE) return -EINVAL; /* element 0 is the number of states */ - if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[0].type) + if (acpi_buffer->package.elements[0].type == ACPI_TYPE_INTEGER) num_states = (unsigned int) acpi_buffer->package.elements[0].integer.value; else @@ -852,15 +856,15 @@ static int nirtfeatures_parse_switch_pie(struct nirtfeatures *nirtfeatures, switch_descriptor = kzalloc( sizeof(struct nirtfeatures_pie_descriptor_switch) + sizeof(int) * (num_states - 1), GFP_KERNEL); - if (NULL == switch_descriptor) + if (switch_descriptor == NULL) return -ENOMEM; switch_descriptor->num_states = num_states; /* parse individual states in elements 1..N-1 */ for (i = 0; i < num_states; i++) { - if (ACPI_TYPE_INTEGER != - acpi_buffer->package.elements[i + 1].type) { + if (acpi_buffer->package.elements[i + 1].type + != ACPI_TYPE_INTEGER) { err = -EINVAL; goto exit; } @@ -871,13 +875,13 @@ static int nirtfeatures_parse_switch_pie(struct nirtfeatures *nirtfeatures, /* create an input class device for this switch */ switch_dev = kzalloc(sizeof(struct nirtfeatures_switch), GFP_KERNEL); - if (NULL == switch_dev) { + if (switch_dev == NULL) { err = -ENOMEM; goto exit; } switch_dev->cdev = input_allocate_device(); - if (NULL == switch_dev->cdev) { + if (switch_dev->cdev == NULL) { err = -ENOMEM; goto exit_dealloc_switch_dev; } @@ -914,7 +918,7 @@ static int nirtfeatures_parse_switch_pie(struct nirtfeatures *nirtfeatures, set_bit(BTN_0, switch_dev->cdev->keybit); err = input_register_device(switch_dev->cdev); - if (0 != err) { + if (err != 0) { input_free_device(switch_dev->cdev); goto exit_dealloc_switch_dev; } @@ -923,7 +927,7 @@ static int nirtfeatures_parse_switch_pie(struct nirtfeatures *nirtfeatures, if (pie->notification_value != 0) { err = nirtfeatures_pie_enable_notifications(nirtfeatures, pie_element, 0, 1); - if (0 != err) { + if (err != 0) { input_unregister_device(switch_dev->cdev); input_free_device(switch_dev->cdev); goto exit_dealloc_switch_dev; @@ -954,16 +958,16 @@ static int nirtfeatures_parse_one_pie(struct nirtfeatures *nirtfeatures, struct nirtfeatures_pie_descriptor pie; unsigned int i; - if (NULL == nirtfeatures || NULL == acpi_buffer) + if (nirtfeatures == NULL || acpi_buffer == NULL) return -EINVAL; /* check for proper type and number of elements */ - if (ACPI_TYPE_PACKAGE != acpi_buffer->type || - 6 != acpi_buffer->package.count) + if (acpi_buffer->type != ACPI_TYPE_PACKAGE || + acpi_buffer->package.count != 6) return -EINVAL; /* element 0 of the package is the name */ - if (ACPI_TYPE_BUFFER == acpi_buffer->package.elements[0].type) { + if (acpi_buffer->package.elements[0].type == ACPI_TYPE_BUFFER) { for (i = 0; i < acpi_buffer->package.elements[0].buffer.length && i < MAX_NAMELEN; i++) { @@ -978,28 +982,28 @@ static int nirtfeatures_parse_one_pie(struct nirtfeatures *nirtfeatures, return -EINVAL; /* element 1 of the package is the PIE class */ - if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[1].type) + if (acpi_buffer->package.elements[1].type == ACPI_TYPE_INTEGER) pie.pie_class = (enum nirtfeatures_pie_class) acpi_buffer->package.elements[1].integer.value; else return -EINVAL; /* element 2 of the package is the PIE type */ - if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[2].type) + if (acpi_buffer->package.elements[2].type == ACPI_TYPE_INTEGER) pie.pie_type = (enum nirtfeatures_pie_type) acpi_buffer->package.elements[2].integer.value; else return -EINVAL; /* element 4 of an package is the visible flag */ - if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[4].type) + if (acpi_buffer->package.elements[4].type == ACPI_TYPE_INTEGER) pie.is_user_visible = (acpi_buffer->package.elements[4].integer.value != 0); else return -EINVAL; /* element 5 of the package is the notification value */ - if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[5].type) + if (acpi_buffer->package.elements[5].type == ACPI_TYPE_INTEGER) pie.notification_value = acpi_buffer->package.elements[5].integer.value; else @@ -1022,7 +1026,6 @@ static int nirtfeatures_parse_one_pie(struct nirtfeatures *nirtfeatures, default: return -EINVAL; - break; } return 0; @@ -1041,7 +1044,7 @@ static int nirtfeatures_populate_pies(struct nirtfeatures *nirtfeatures) unsigned int i; unsigned int err = 0; - if (NULL == nirtfeatures) + if (nirtfeatures == NULL) return -EINVAL; /* get the PIE descriptor buffer from DSDT */ @@ -1050,13 +1053,13 @@ static int nirtfeatures_populate_pies(struct nirtfeatures *nirtfeatures) return -1; acpi_buffer = (union acpi_object *) result_buffer; - if (ACPI_TYPE_PACKAGE != acpi_buffer->type) { + if (acpi_buffer->type != ACPI_TYPE_PACKAGE) { err = -1; goto exit; } /* the first element of the package is the caps version */ - if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[0].type) + if (acpi_buffer->package.elements[0].type == ACPI_TYPE_INTEGER) pie_caps_version = (unsigned int) acpi_buffer->package.elements[0].integer.value; else { @@ -1073,7 +1076,7 @@ static int nirtfeatures_populate_pies(struct nirtfeatures *nirtfeatures) } /* the second element of the package is the number of PIEs */ - if (ACPI_TYPE_INTEGER == acpi_buffer->package.elements[1].type) + if (acpi_buffer->package.elements[1].type == ACPI_TYPE_INTEGER) num_elements = (unsigned int) acpi_buffer->package.elements[1].integer.value; else { @@ -1086,7 +1089,7 @@ static int nirtfeatures_populate_pies(struct nirtfeatures *nirtfeatures) err = nirtfeatures_parse_one_pie(nirtfeatures, pie_caps_version, i - 2, &(acpi_buffer->package.elements[i])); - if (0 != err) + if (err != 0) break; } @@ -1104,7 +1107,7 @@ static int nirtfeatures_create_leds(struct nirtfeatures *nirtfeatures) nirtfeatures_leds_common[i].nirtfeatures = nirtfeatures; - if (0 == nirtfeatures_leds_common[i].cdev.max_brightness) + if (nirtfeatures_leds_common[i].cdev.max_brightness == 0) nirtfeatures_leds_common[i].cdev.max_brightness = 1; nirtfeatures_leds_common[i].cdev.brightness_set = @@ -1425,7 +1428,7 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) spin_lock_init(&nirtfeatures->lock); err = nirtfeatures_populate_pies(nirtfeatures); - if (0 != err) { + if (err != 0) { nirtfeatures_acpi_remove(device); return err; } @@ -1438,13 +1441,13 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) err = sysfs_create_files(&nirtfeatures->acpi_device->dev.kobj, nirtfeatures_attrs); - if (0 != err) { + if (err != 0) { nirtfeatures_acpi_remove(device); return err; } err = nirtfeatures_create_leds(nirtfeatures); - if (0 != err) { + if (err != 0) { nirtfeatures_acpi_remove(device); return err; } @@ -1457,7 +1460,7 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) if (nirtfeatures->has_wifi) { err = nirtfeatures_wifi_regulator_init(&device->dev, nirtfeatures); - if (0 != err) { + if (err != 0) { nirtfeatures_acpi_remove(device); return err; } From bffc6087243dbe3233e3409efebfcb5fce489b92 Mon Sep 17 00:00:00 2001 From: Akash Mankar Date: Fri, 17 Feb 2017 16:52:58 -0600 Subject: [PATCH 24/84] nirtfeatures: Implement serial IRQ mechanism for user push button. This commit assumes that BIOS will implement a change to add a new field to PIEC capability structure. Also bumping the capabilities version by 1. Adding IRQ mechanism for user Push button instead of GPIO This change registers and requests an IRQ for User push button.Adds a new handler pushbutton_interrupt_handler() to handle the interrupt when the button is pressed or released. Adds a new field to struct nirtfeatures_pie_descriptor called notification_method. If this field is 1, it indicates interrupt mechanism. We check this field only for caps version=3 and pie_type=switch. Also bumps the MAX_PIE_CAPS_VERSION to 3. The kernel and BIOS have to be updated at the same time in order for this change to be successful. If kernel is updated first on a controller, user button will simply not function but will have no other side effects. If BIOS is updated first, then system will end in kernel panic and reboot constantly. Signed-off-by: Akash Mankar Signed-off-by: Brad Mouring Acked-by: Zach Hindes Acked-by: Aaron Rossetto Natinst-ReviewBoard-ID: 174593 --- drivers/misc/nirtfeatures.c | 85 ++++++++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 5 deletions(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 41b2c63e86c24..53c8b012ebbd0 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -13,6 +13,7 @@ */ #include +#include #include #include #include @@ -73,7 +74,9 @@ #define MAX_NAMELEN 64 #define MAX_NODELEN 128 #define MIN_PIE_CAPS_VERSION 2 -#define MAX_PIE_CAPS_VERSION 2 +#define MAX_PIE_CAPS_VERSION 3 +#define NOTIFY_METHOD_INTERRUPT 1 +#define NOTIFY_METHOD_GPIO 0 enum nirtfeatures_pie_class { PIE_CLASS_INPUT = 0, @@ -92,6 +95,8 @@ struct nirtfeatures_pie_descriptor { enum nirtfeatures_pie_type pie_type; bool is_user_visible; unsigned int notification_value; + /* notification_method applicable only for caps version 3 & above */ + unsigned int notification_method; }; struct nirtfeatures_pie_descriptor_led_color { @@ -122,6 +127,7 @@ struct nirtfeatures { const char *bpstring; bool has_wifi; struct regulator_dev *reg_dev; + unsigned int irq; }; struct nirtfeatures_led { @@ -961,9 +967,14 @@ static int nirtfeatures_parse_one_pie(struct nirtfeatures *nirtfeatures, if (nirtfeatures == NULL || acpi_buffer == NULL) return -EINVAL; - /* check for proper type and number of elements */ + /* + * check for proper type and number of elements + * caps_version 2 or less, support only 6 elements in package + * caps_version 3 or more, support only up to 7 elements in package + */ if (acpi_buffer->type != ACPI_TYPE_PACKAGE || - acpi_buffer->package.count != 6) + (acpi_buffer->package.count != 6 && pie_caps_version < 3) || + (acpi_buffer->package.count > 7 && pie_caps_version >= 3)) return -EINVAL; /* element 0 of the package is the name */ @@ -1005,10 +1016,23 @@ static int nirtfeatures_parse_one_pie(struct nirtfeatures *nirtfeatures, /* element 5 of the package is the notification value */ if (acpi_buffer->package.elements[5].type == ACPI_TYPE_INTEGER) pie.notification_value = - acpi_buffer->package.elements[5].integer.value; + acpi_buffer->package.elements[5].integer.value; else return -EINVAL; + /* + * element 6 of the package is notification method + * used only for pie type switch and caps_Version >= 3 + * don't worry about it for lower caps versions. + */ + if (pie_caps_version >= 3 && pie.pie_type == PIE_TYPE_SWITCH) { + if (acpi_buffer->package.elements[6].type == ACPI_TYPE_INTEGER) + pie.notification_method = + acpi_buffer->package.elements[6].integer.value; + else + return -EINVAL; + } + /* parse the type-specific descriptor in element 3 */ switch (pie.pie_type) { case PIE_TYPE_LED: @@ -1175,13 +1199,64 @@ static void nirtfeatures_remove_switch_pies(struct nirtfeatures *nirtfeatures) spin_unlock(&nirtfeatures->lock); } +/* IRQ Handler for User push button */ +static irqreturn_t pushbutton_interrupt_handler(int irq, void *data) +{ + /* find the switch PIE for which this interrupt was generated */ + struct nirtfeatures_switch *iter; + struct nirtfeatures *nirtfeatures = (struct nirtfeatures *)data; + int state = 0; + + spin_lock(&nirtfeatures->lock); + list_for_each_entry(iter, &nirtfeatures_switch_pie_list, node) { + if (iter->pie_descriptor.notification_method == NOTIFY_METHOD_INTERRUPT && + iter->pie_descriptor.notification_value == irq) { + /* query instantaneous switch state */ + if (!nirtfeatures_pie_get_state(iter->nirtfeatures, + iter->pie_location.element, + iter->pie_location.subelement, + &state)) { + /* push current state of switch */ + input_report_key(iter->cdev, BTN_0, !!state); + input_sync(iter->cdev); + } + spin_unlock(&nirtfeatures->lock); + return IRQ_HANDLED; + } + } + spin_unlock(&nirtfeatures->lock); + return IRQ_NONE; +} + /* ACPI driver */ static acpi_status nirtfeatures_resources(struct acpi_resource *res, void *data) { struct nirtfeatures *nirtfeatures = data; + int err; switch (res->type) { + case ACPI_RESOURCE_TYPE_IRQ: + if (nirtfeatures->irq != 0) { + dev_err(&nirtfeatures->acpi_device->dev, + "too many IRQ resources\n"); + return AE_ERROR; + } + + nirtfeatures->irq = res->data.irq.interrupts[0]; + + err = devm_request_irq(&nirtfeatures->acpi_device->dev, + nirtfeatures->irq, + pushbutton_interrupt_handler, + 0 /*irq_flags*/, MODULE_NAME, + nirtfeatures); + if (err) { + dev_err(&nirtfeatures->acpi_device->dev, + "failed to request IRQ (err %d)\n", err); + return AE_ERROR; + } + return AE_OK; + case ACPI_RESOURCE_TYPE_IO: if ((nirtfeatures->io_base != 0) || (nirtfeatures->io_size != 0)) { @@ -1385,7 +1460,7 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) nirtfeatures->acpi_device = device; acpi_ret = acpi_walk_resources(device->handle, METHOD_NAME__CRS, - nirtfeatures_resources, nirtfeatures); + nirtfeatures_resources, nirtfeatures); if (ACPI_FAILURE(acpi_ret) || (nirtfeatures->io_base == 0) || From 6652c32c59eaa5011795dd1a748c46f07fb15ead Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Wed, 15 Feb 2017 09:12:38 -0600 Subject: [PATCH 25/84] nirtfeatures: Add Ironclad reset source string On Fire Eagle, the Ironclad ASIC has an internal watchdog which will reset the chip if the its firmware hangs. Unless we're in button- directed safemode, this will also reset the whole system. Add this reset reason to our strings so we can tell when this happens. Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: James Minor Acked-by: Zach Brown Acked-by: Akash Mankar Natinst-ReviewBoard-ID: 176049 --- drivers/misc/nirtfeatures.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 53c8b012ebbd0..83711f1119c79 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -219,7 +219,7 @@ static ssize_t nirtfeatures_backplane_id_get(struct device *dev, static DEVICE_ATTR(backplane_id, S_IRUGO, nirtfeatures_backplane_id_get, NULL); static const char * const nirtfeatures_reset_source_strings[] = { - "button", "processor", "fpga", "watchdog", "software", + "button", "processor", "fpga", "watchdog", "software", "ironclad", }; static ssize_t nirtfeatures_reset_source_get(struct device *dev, From a68069fb6fe73dd004b803afb30e9ca6b3ce8c7e Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Thu, 16 Mar 2017 14:13:38 -0500 Subject: [PATCH 26/84] nirtfeatures: Use managed resource allocation Adding a new ACPI resource to device BIOS exposed the fact that our error handling on a failed device probe is very broken. To fix this, use managed allocation (devm_*) for all resources which support it. Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: James Minor Acked-by: Zach Brown Acked-by: Akash Mankar Natinst-ReviewBoard-ID: 176049 --- drivers/misc/nirtfeatures.c | 191 +++++++++--------------------------- 1 file changed, 48 insertions(+), 143 deletions(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 83711f1119c79..a97755f965567 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -776,7 +776,9 @@ static int nirtfeatures_parse_led_pie( return -EINVAL; /* create an LED class device for this LED */ - led_dev = kzalloc(sizeof(struct nirtfeatures_led), GFP_KERNEL); + led_dev = devm_kzalloc(&nirtfeatures->acpi_device->dev, + sizeof(struct nirtfeatures_led), + GFP_KERNEL); if (led_dev == NULL) return -ENOMEM; @@ -817,12 +819,10 @@ static int nirtfeatures_parse_led_pie( led_dev->pie_location.element = pie_element; led_dev->pie_location.subelement = i; - err = led_classdev_register(&nirtfeatures->acpi_device->dev, - &led_dev->cdev); - if (err != 0) { - kfree(led_dev); + err = devm_led_classdev_register(&nirtfeatures->acpi_device->dev, + &led_dev->cdev); + if (err) return err; - } list_add_tail(&led_dev->node, &nirtfeatures_led_pie_list); } @@ -844,6 +844,7 @@ static int nirtfeatures_parse_switch_pie(struct nirtfeatures *nirtfeatures, struct nirtfeatures_pie_descriptor_switch *switch_descriptor = NULL; struct nirtfeatures_switch *switch_dev = NULL; int err = 0; + struct device *dev = &nirtfeatures->acpi_device->dev; if (nirtfeatures == NULL || pie == NULL || acpi_buffer == NULL) return -EINVAL; @@ -859,9 +860,9 @@ static int nirtfeatures_parse_switch_pie(struct nirtfeatures *nirtfeatures, return -EINVAL; /* allocate storage for switch descriptor */ - switch_descriptor = kzalloc( - sizeof(struct nirtfeatures_pie_descriptor_switch) + - sizeof(int) * (num_states - 1), GFP_KERNEL); + switch_descriptor = devm_kzalloc(dev, sizeof(*switch_descriptor) + + sizeof(int) * (num_states - 1), + GFP_KERNEL); if (switch_descriptor == NULL) return -ENOMEM; @@ -869,28 +870,23 @@ static int nirtfeatures_parse_switch_pie(struct nirtfeatures *nirtfeatures, /* parse individual states in elements 1..N-1 */ for (i = 0; i < num_states; i++) { - if (acpi_buffer->package.elements[i + 1].type - != ACPI_TYPE_INTEGER) { - err = -EINVAL; - goto exit; - } + if (acpi_buffer->package.elements[i + 1].type != + ACPI_TYPE_INTEGER) + return -EINVAL; switch_descriptor->state_value[i] = (int) acpi_buffer->package.elements[i + 1].integer.value; } /* create an input class device for this switch */ - switch_dev = kzalloc(sizeof(struct nirtfeatures_switch), GFP_KERNEL); - if (switch_dev == NULL) { - err = -ENOMEM; - goto exit; - } + switch_dev = devm_kzalloc(dev, sizeof(struct nirtfeatures_switch), + GFP_KERNEL); + if (switch_dev == NULL) + return -ENOMEM; - switch_dev->cdev = input_allocate_device(); - if (switch_dev->cdev == NULL) { - err = -ENOMEM; - goto exit_dealloc_switch_dev; - } + switch_dev->cdev = devm_input_allocate_device(dev); + if (switch_dev->cdev == NULL) + return -ENOMEM; switch_dev->nirtfeatures = nirtfeatures; switch_dev->pie_location.element = pie_element; @@ -924,32 +920,21 @@ static int nirtfeatures_parse_switch_pie(struct nirtfeatures *nirtfeatures, set_bit(BTN_0, switch_dev->cdev->keybit); err = input_register_device(switch_dev->cdev); - if (err != 0) { - input_free_device(switch_dev->cdev); - goto exit_dealloc_switch_dev; - } + if (err) + return err; /* if this PIE supports notifications, enable them now */ if (pie->notification_value != 0) { err = nirtfeatures_pie_enable_notifications(nirtfeatures, - pie_element, 0, 1); - if (err != 0) { - input_unregister_device(switch_dev->cdev); - input_free_device(switch_dev->cdev); - goto exit_dealloc_switch_dev; - } + pie_element, 0, 1); + if (err) + return err; } /* add the new device to our list of switch PIEs */ list_add_tail(&switch_dev->node, &nirtfeatures_switch_pie_list); - goto exit; - -exit_dealloc_switch_dev: - kfree(switch_dev); -exit: - kfree(switch_descriptor); - return err; + return 0; } @@ -1140,8 +1125,8 @@ static int nirtfeatures_create_leds(struct nirtfeatures *nirtfeatures) nirtfeatures_leds_common[i].cdev.brightness_get = nirtfeatures_led_brightness_get; - err = led_classdev_register(&nirtfeatures->acpi_device->dev, - &nirtfeatures_leds_common[i].cdev); + err = devm_led_classdev_register(&nirtfeatures->acpi_device->dev, + &nirtfeatures_leds_common[i].cdev); if (err) return err; } @@ -1149,56 +1134,6 @@ static int nirtfeatures_create_leds(struct nirtfeatures *nirtfeatures) return 0; } -static void nirtfeatures_remove_leds(struct nirtfeatures *nirtfeatures) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(nirtfeatures_leds_common); ++i) - led_classdev_unregister(&nirtfeatures_leds_common[i].cdev); -} - -static void nirtfeatures_remove_led_pies(struct nirtfeatures *nirtfeatures) -{ - struct nirtfeatures_led *cdev_iter; - struct nirtfeatures_led *temp; - - spin_lock(&nirtfeatures->lock); - - /* walk the list of non-fixed LEDs and unregister/free their devices */ - list_for_each_entry_safe( - cdev_iter, temp, &nirtfeatures_led_pie_list, node) { - led_classdev_unregister(&cdev_iter->cdev); - kfree(cdev_iter); - } - - spin_unlock(&nirtfeatures->lock); -} - -static void nirtfeatures_remove_switch_pies(struct nirtfeatures *nirtfeatures) -{ - struct nirtfeatures_switch *cdev_iter; - struct nirtfeatures_switch *temp; - - spin_lock(&nirtfeatures->lock); - - /* walk the list of switch devices and unregister/free each one */ - list_for_each_entry_safe( - cdev_iter, temp, &nirtfeatures_switch_pie_list, node) { - /* disable notifications for this PIE if supported */ - if (cdev_iter->pie_descriptor.notification_value != 0) { - nirtfeatures_pie_enable_notifications(nirtfeatures, - cdev_iter->pie_location.element, - cdev_iter->pie_location.subelement, - 0); - } - input_unregister_device(cdev_iter->cdev); - input_free_device(cdev_iter->cdev); - kfree(cdev_iter); - } - - spin_unlock(&nirtfeatures->lock); -} - /* IRQ Handler for User push button */ static irqreturn_t pushbutton_interrupt_handler(int irq, void *data) { @@ -1323,26 +1258,7 @@ static void nirtfeatures_acpi_notify(struct acpi_device *device, u32 event) static int nirtfeatures_acpi_remove(struct acpi_device *device) { - struct nirtfeatures *nirtfeatures = device->driver_data; - - nirtfeatures_remove_leds(nirtfeatures); - - nirtfeatures_remove_led_pies(nirtfeatures); - nirtfeatures_remove_switch_pies(nirtfeatures); - - sysfs_remove_files(&nirtfeatures->acpi_device->dev.kobj, - nirtfeatures_attrs); - - if (nirtfeatures->reg_dev) - regulator_unregister(nirtfeatures->reg_dev); - - if ((nirtfeatures->io_base != 0) && - (nirtfeatures->io_size == NIRTF_IO_SIZE)) - release_region(nirtfeatures->io_base, nirtfeatures->io_size); - - device->driver_data = NULL; - - kfree(nirtfeatures); + sysfs_remove_files(&device->dev.kobj, nirtfeatures_attrs); return 0; } @@ -1433,8 +1349,9 @@ static int nirtfeatures_wifi_regulator_init(struct device *dev, cfg.dev = dev; cfg.init_data = &wifi_reset_init_data; cfg.driver_data = nirtfeatures; - reg_dev = regulator_register(&nirtfeatures_wifi_regulator_desc, - &cfg); + reg_dev = devm_regulator_register(dev, + &nirtfeatures_wifi_regulator_desc, + &cfg); if (IS_ERR(reg_dev)) { pr_err("Failed to register vmmc regulator for wifi\n"); return -ENODEV; @@ -1450,7 +1367,8 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) u8 bpinfo; int err; - nirtfeatures = kzalloc(sizeof(*nirtfeatures), GFP_KERNEL); + nirtfeatures = devm_kzalloc(&device->dev, sizeof(*nirtfeatures), + GFP_KERNEL); if (!nirtfeatures) return -ENOMEM; @@ -1464,16 +1382,12 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) if (ACPI_FAILURE(acpi_ret) || (nirtfeatures->io_base == 0) || - (nirtfeatures->io_size != NIRTF_IO_SIZE)) { - nirtfeatures_acpi_remove(device); + (nirtfeatures->io_size != NIRTF_IO_SIZE)) return -ENODEV; - } - if (!request_region(nirtfeatures->io_base, nirtfeatures->io_size, - MODULE_NAME)) { - nirtfeatures_acpi_remove(device); + if (!devm_request_region(&device->dev, nirtfeatures->io_base, + nirtfeatures->io_size, MODULE_NAME)) return -EBUSY; - } bpinfo = inb(nirtfeatures->io_base + NIRTF_PLATFORM_MISC); @@ -1503,10 +1417,8 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) spin_lock_init(&nirtfeatures->lock); err = nirtfeatures_populate_pies(nirtfeatures); - if (err != 0) { - nirtfeatures_acpi_remove(device); + if (err) return err; - } nirtfeatures->revision[0] = inb(nirtfeatures->io_base + NIRTF_YEAR); nirtfeatures->revision[1] = inb(nirtfeatures->io_base + NIRTF_MONTH); @@ -1514,33 +1426,26 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) nirtfeatures->revision[3] = inb(nirtfeatures->io_base + NIRTF_HOUR); nirtfeatures->revision[4] = inb(nirtfeatures->io_base + NIRTF_MINUTE); - err = sysfs_create_files(&nirtfeatures->acpi_device->dev.kobj, - nirtfeatures_attrs); - if (err != 0) { - nirtfeatures_acpi_remove(device); + err = nirtfeatures_create_leds(nirtfeatures); + if (err) return err; + + if (nirtfeatures->has_wifi) { + err = nirtfeatures_wifi_regulator_init(&device->dev, + nirtfeatures); + if (err) + return err; } - err = nirtfeatures_create_leds(nirtfeatures); - if (err != 0) { - nirtfeatures_acpi_remove(device); + err = sysfs_create_files(&device->dev.kobj, nirtfeatures_attrs); + if (err) return err; - } dev_info(&nirtfeatures->acpi_device->dev, "IO range 0x%04X-0x%04X\n", nirtfeatures->io_base, nirtfeatures->io_base + nirtfeatures->io_size - 1); - if (nirtfeatures->has_wifi) { - err = nirtfeatures_wifi_regulator_init(&device->dev, - nirtfeatures); - if (err != 0) { - nirtfeatures_acpi_remove(device); - return err; - } - } - return 0; } From 6cf75b34e1f18cb8fdaf053a93bf7850dfdc6d0f Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Wed, 14 Mar 2018 18:22:42 -0500 Subject: [PATCH 27/84] nirtfeatures: Add Swordfish backplane ID Add the new Swordfish backplane ID to stop errors on boot. Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: Zach Brown Acked-by: Nathan Sullivan Acked-by: Julia Cartwright Natinst-ReviewBoard-ID: 227200 --- drivers/misc/nirtfeatures.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index a97755f965567..76f06f82cb537 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -47,6 +47,7 @@ #define NIRTF_PLATFORM_MISC_ID_MASK 0x07 #define NIRTF_PLATFORM_MISC_ID_MANHATTAN 0 +#define NIRTF_PLATFORM_MISC_ID_SWORDFISH 1 #define NIRTF_PLATFORM_MISC_ID_FIRE_EAGLE 2 #define NIRTF_PLATFORM_MISC_ID_HAMMERHEAD 4 #define NIRTF_PLATFORM_MISC_ID_WINGHEAD 5 @@ -1400,6 +1401,9 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) case NIRTF_PLATFORM_MISC_ID_FIRE_EAGLE: nirtfeatures->bpstring = "Fire Eagle"; break; + case NIRTF_PLATFORM_MISC_ID_SWORDFISH: + nirtfeatures->bpstring = "Swordfish"; + break; case NIRTF_PLATFORM_MISC_ID_HAMMERHEAD: nirtfeatures->bpstring = "Hammerhead"; break; From b48d1882b1482ab89ada2f7aa51429204bf708aa Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Wed, 14 Mar 2018 18:23:30 -0500 Subject: [PATCH 28/84] nirtfeatures: Only register bi-color LEDs on supported targets Unlike all previous controllers, Swordfish targets do not have bi-color power and status LEDs. Check our backplane ID and don't register the second color if we're on a Swordfish. Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: Zach Brown Acked-by: Nathan Sullivan Acked-by: Julia Cartwright Natinst-ReviewBoard-ID: 227200 Natinst-CAR-ID: 691081 --- drivers/misc/nirtfeatures.c | 130 +++++++++++++++++++++++++----------- 1 file changed, 91 insertions(+), 39 deletions(-) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 76f06f82cb537..6677318325c0a 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -125,10 +125,17 @@ struct nirtfeatures { u16 io_size; spinlock_t lock; u8 revision[5]; - const char *bpstring; bool has_wifi; struct regulator_dev *reg_dev; unsigned int irq; + struct nirtfeatures_desc *desc; +}; + +struct nirtfeatures_desc { + unsigned int backplane_id; + const char *name; + struct nirtfeatures_led *leds; + unsigned int num_leds; }; struct nirtfeatures_led { @@ -214,7 +221,7 @@ static ssize_t nirtfeatures_backplane_id_get(struct device *dev, struct acpi_device *acpi_device = to_acpi_device(dev); struct nirtfeatures *nirtfeatures = acpi_device->driver_data; - return sprintf(buf, "%s\n", nirtfeatures->bpstring); + return sprintf(buf, "%s\n", nirtfeatures->desc->name); } static DEVICE_ATTR(backplane_id, S_IRUGO, nirtfeatures_backplane_id_get, NULL); @@ -457,7 +464,7 @@ nirtfeatures_led_brightness_get(struct led_classdev *led_cdev) return (data & led->mask) ? LED_FULL : LED_OFF; } -static struct nirtfeatures_led nirtfeatures_leds_common[] = { +static struct nirtfeatures_led nirtfeatures_leds[] = { { { .name = CONFIG_NI_LED_PREFIX ":status:red", @@ -488,7 +495,67 @@ static struct nirtfeatures_led nirtfeatures_leds_common[] = { }, .address = NIRTF_SYSTEM_LEDS, .mask = NIRTF_SYSTEM_LEDS_POWER_YELLOW, + } +}; + +static struct nirtfeatures_led nirtfeatures_leds_monochrome[] = { + { + { + .name = CONFIG_NI_LED_PREFIX ":status:yellow", + .max_brightness = 0xFFFF, + }, + .address = NIRTF_SYSTEM_LEDS, + .mask = NIRTF_SYSTEM_LEDS_STATUS_YELLOW, + .pattern_hi_addr = NIRTF_STATUS_LED_SHIFT1, + .pattern_lo_addr = NIRTF_STATUS_LED_SHIFT0, }, + { + { + .name = CONFIG_NI_LED_PREFIX ":power:green", + }, + .address = NIRTF_SYSTEM_LEDS, + .mask = NIRTF_SYSTEM_LEDS_POWER_GREEN, + } +}; + +static struct nirtfeatures_desc nirtfeatures_descs[] = { + { + .backplane_id = NIRTF_PLATFORM_MISC_ID_MANHATTAN, + .name = "Manhattan", + .leds = nirtfeatures_leds, + .num_leds = ARRAY_SIZE(nirtfeatures_leds), + }, + { + .backplane_id = NIRTF_PLATFORM_MISC_ID_FIRE_EAGLE, + .name = "Fire Eagle", + .leds = nirtfeatures_leds, + .num_leds = ARRAY_SIZE(nirtfeatures_leds), + }, + { + .backplane_id = NIRTF_PLATFORM_MISC_ID_SWORDFISH, + .name = "Swordfish", + .leds = nirtfeatures_leds_monochrome, + .num_leds = ARRAY_SIZE(nirtfeatures_leds_monochrome), + }, + { + .backplane_id = NIRTF_PLATFORM_MISC_ID_HAMMERHEAD, + .name = "Hammerhead", + .leds = nirtfeatures_leds, + .num_leds = ARRAY_SIZE(nirtfeatures_leds), + }, + { + .backplane_id = NIRTF_PLATFORM_MISC_ID_WINGHEAD, + .name = "Winghead", + .leds = nirtfeatures_leds, + .num_leds = ARRAY_SIZE(nirtfeatures_leds), + } +}; + +static struct nirtfeatures_desc nirtfeatures_desc_unknown = { + .backplane_id = 0xf, + .name = "Unknown", + .leds = nirtfeatures_leds, + .num_leds = ARRAY_SIZE(nirtfeatures_leds), }; /*===================================================================== @@ -907,7 +974,7 @@ static int nirtfeatures_parse_switch_pie(struct nirtfeatures *nirtfeatures, } snprintf(switch_dev->phys_location_string, MAX_NODELEN, "%s/%s/%s", - CONFIG_NI_LED_PREFIX, nirtfeatures->bpstring, pie->name); + CONFIG_NI_LED_PREFIX, nirtfeatures->desc->name, pie->name); switch_dev->cdev->name = switch_dev->name_string; switch_dev->cdev->phys = switch_dev->phys_location_string; @@ -1110,24 +1177,20 @@ static int nirtfeatures_populate_pies(struct nirtfeatures *nirtfeatures) static int nirtfeatures_create_leds(struct nirtfeatures *nirtfeatures) { - int i; - int err; - - for (i = 0; i < ARRAY_SIZE(nirtfeatures_leds_common); ++i) { - - nirtfeatures_leds_common[i].nirtfeatures = nirtfeatures; + struct nirtfeatures_led *leds = nirtfeatures->desc->leds; + int i, err; - if (nirtfeatures_leds_common[i].cdev.max_brightness == 0) - nirtfeatures_leds_common[i].cdev.max_brightness = 1; + for (i = 0; i < nirtfeatures->desc->num_leds; i++) { + leds[i].nirtfeatures = nirtfeatures; - nirtfeatures_leds_common[i].cdev.brightness_set = - nirtfeatures_led_brightness_set; + if (leds[i].cdev.max_brightness == 0) + leds[i].cdev.max_brightness = 1; - nirtfeatures_leds_common[i].cdev.brightness_get = - nirtfeatures_led_brightness_get; + leds[i].cdev.brightness_set = nirtfeatures_led_brightness_set; + leds[i].cdev.brightness_get = nirtfeatures_led_brightness_get; err = devm_led_classdev_register(&nirtfeatures->acpi_device->dev, - &nirtfeatures_leds_common[i].cdev); + &leds[i].cdev); if (err) return err; } @@ -1366,7 +1429,7 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) struct nirtfeatures *nirtfeatures; acpi_status acpi_ret; u8 bpinfo; - int err; + int err, i; nirtfeatures = devm_kzalloc(&device->dev, sizeof(*nirtfeatures), GFP_KERNEL); @@ -1394,28 +1457,17 @@ static int nirtfeatures_acpi_add(struct acpi_device *device) bpinfo &= NIRTF_PLATFORM_MISC_ID_MASK; - switch (bpinfo) { - case NIRTF_PLATFORM_MISC_ID_MANHATTAN: - nirtfeatures->bpstring = "Manhattan"; - break; - case NIRTF_PLATFORM_MISC_ID_FIRE_EAGLE: - nirtfeatures->bpstring = "Fire Eagle"; - break; - case NIRTF_PLATFORM_MISC_ID_SWORDFISH: - nirtfeatures->bpstring = "Swordfish"; - break; - case NIRTF_PLATFORM_MISC_ID_HAMMERHEAD: - nirtfeatures->bpstring = "Hammerhead"; - break; - case NIRTF_PLATFORM_MISC_ID_WINGHEAD: - nirtfeatures->bpstring = "Winghead"; - break; - default: + for (i = 0; i < ARRAY_SIZE(nirtfeatures_descs); i++) { + if (bpinfo == nirtfeatures_descs[i].backplane_id) { + nirtfeatures->desc = &nirtfeatures_descs[i]; + break; + } + } + + if (!nirtfeatures->desc) { dev_err(&nirtfeatures->acpi_device->dev, - "Unrecognized backplane type %u\n", - bpinfo); - nirtfeatures->bpstring = "Unknown"; - break; + "Unrecognized backplane ID %u\n", bpinfo); + nirtfeatures->desc = &nirtfeatures_desc_unknown; } spin_lock_init(&nirtfeatures->lock); From 11acea42a33126bccb79111f77950aa2b13c63fd Mon Sep 17 00:00:00 2001 From: Gratian Crisan Date: Fri, 20 Aug 2021 15:47:31 -0500 Subject: [PATCH 29/84] nirtfeatures: Automatically select support for NEW_LEDS Prior to commit 7142f92412c1 ("wireless: carl9170: fix LEDS build errors & warnings") support for MAC80211_LEDS, NEW_LEDS and LEDS_CLASS config options was automatically enabled by having CONFIG_CARL9170=m in nati_x86_64_defconfig. The aforementioned commit removed the automatic selection in favor of CARL9170_LEDS config option declaring a dependency on MAC80211_LEDS. This results in linker errors if the nirtfeatures driver is enabled via CONFIG_NI_RT_FEATURES since this driver makes use of functionality provided by the NEW_LEDS and LEDS_CLASS configuration options: LD .tmp_vmlinux.kallsyms1 ld: drivers/misc/nirtfeatures.o: in function `nirtfeatures_parse_led_pie.isra.0': nirtfeatures.c:(.text+0x103b): undefined reference to `devm_led_classdev_register_ext' ld: drivers/misc/nirtfeatures.o: in function `nirtfeatures_acpi_add': nirtfeatures.c:(.text+0x14ad): undefined reference to `devm_led_classdev_register_ext' make: *** [Makefile:1179: vmlinux] Error 1 Automatically select support for NEW_LEDS and by extension LEDS_CLASS if NI_RT_FEATURES is enabled. Signed-off-by: Gratian Crisan --- drivers/misc/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index a878fd022b9c4..2037e97ca98f1 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -474,6 +474,7 @@ config NI_RT_FEATURES bool "NI 903x/913x support" depends on X86 && ACPI select REGULATOR + select NEW_LEDS help This driver exposes LEDs and other features of NI 903x/913x Real-Time controllers. From 1802e65b911ca5d8ff789b1cb0a6dc73299ab5cd Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Fri, 30 Nov 2018 14:34:02 -0600 Subject: [PATCH 30/84] nirtfeatures: Add Dogfish backplane ID and LED info This family uses the same monochrome LEDs as Swordfish. Signed-off-by: Kyle Roeschley Acked-by: Brandon Streiff Acked-by: Gratian Crisan Acked-by: Julia Cartwright Natinst-ReviewBoard-ID: 273119 --- drivers/misc/nirtfeatures.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/misc/nirtfeatures.c b/drivers/misc/nirtfeatures.c index 6677318325c0a..08726721a534e 100644 --- a/drivers/misc/nirtfeatures.c +++ b/drivers/misc/nirtfeatures.c @@ -49,6 +49,7 @@ #define NIRTF_PLATFORM_MISC_ID_MANHATTAN 0 #define NIRTF_PLATFORM_MISC_ID_SWORDFISH 1 #define NIRTF_PLATFORM_MISC_ID_FIRE_EAGLE 2 +#define NIRTF_PLATFORM_MISC_ID_DOGFISH 3 #define NIRTF_PLATFORM_MISC_ID_HAMMERHEAD 4 #define NIRTF_PLATFORM_MISC_ID_WINGHEAD 5 @@ -537,6 +538,12 @@ static struct nirtfeatures_desc nirtfeatures_descs[] = { .leds = nirtfeatures_leds_monochrome, .num_leds = ARRAY_SIZE(nirtfeatures_leds_monochrome), }, + { + .backplane_id = NIRTF_PLATFORM_MISC_ID_DOGFISH, + .name = "Dogfish", + .leds = nirtfeatures_leds_monochrome, + .num_leds = ARRAY_SIZE(nirtfeatures_leds_monochrome), + }, { .backplane_id = NIRTF_PLATFORM_MISC_ID_HAMMERHEAD, .name = "Hammerhead", From 7f2b7ac267154e1a91f5f6192327f8b63aedb0c4 Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Mon, 16 Dec 2013 10:44:59 -0600 Subject: [PATCH 31/84] niwatchdog: Added NI Watchdog driver Added an NI Watchdog driver. This is an ACPI device that exposes the NI Watchdog hardware interface. Not all of the proposed features of the device work as expected. Development work on this device by the hardware team is currently not a high priority. These issues will be addressed once the hardware team gets back to this device. Signed-off-by: Jeff Westfahl --- drivers/misc/Kconfig | 9 ++ drivers/misc/Makefile | 1 + drivers/misc/niwatchdog.c | 254 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 264 insertions(+) create mode 100644 drivers/misc/niwatchdog.c diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 2037e97ca98f1..0bb083629ed77 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -492,6 +492,15 @@ config NI_LED_PREFIX If unsure, use the default. +config NI_WATCHDOG + bool "NI Watchdog support for NI 903x/913x" + depends on X86 && ACPI + help + This driver exposes the NI Watchdog feature of NI 903x/913x Real-Time + controllers. + + If unsure, say N (but it's safe to say "Y"). + source "drivers/misc/c2port/Kconfig" source "drivers/misc/eeprom/Kconfig" source "drivers/misc/cb710/Kconfig" diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 5d1f171bc7b11..b10ccba1d91a0 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -60,3 +60,4 @@ obj-$(CONFIG_XILINX_SDFEC) += xilinx_sdfec.o obj-$(CONFIG_HISI_HIKEY_USB) += hisi_hikey_usb.o obj-$(CONFIG_HI6421V600_IRQ) += hi6421v600-irq.o obj-$(CONFIG_NI_RT_FEATURES) += nirtfeatures.o +obj-$(CONFIG_NI_WATCHDOG) += niwatchdog.o diff --git a/drivers/misc/niwatchdog.c b/drivers/misc/niwatchdog.c new file mode 100644 index 0000000000000..d488b49e10a5f --- /dev/null +++ b/drivers/misc/niwatchdog.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2013 National Instruments 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. + * + * 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 + +#define MODULE_NAME "niwatchdog" + +#define NIWD_CONTROL 0x01 +#define NIWD_COUNTER2 0x02 +#define NIWD_COUNTER1 0x03 +#define NIWD_COUNTER0 0x04 +#define NIWD_SEED2 0x05 +#define NIWD_SEED1 0x06 +#define NIWD_SEED0 0x07 + +#define NIWD_IO_SIZE 0x08 + +#define NIWD_CONTROL_MODE 0x80 +#define NIWD_CONTROL_RESET 0x02 + +struct niwatchdog { + struct acpi_device *acpi_device; + u16 io_base; + u16 io_size; + u32 irq; + spinlock_t lock; +}; + +static ssize_t niwatchdog_wdmode_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct niwatchdog *niwatchdog = acpi_device->driver_data; + u8 data; + + data = inb(niwatchdog->io_base + NIWD_CONTROL); + + data &= NIWD_CONTROL_MODE; + + return sprintf(buf, "%s\n", data ? "boot" : "user"); +} + +static ssize_t niwatchdog_wdmode_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct niwatchdog *niwatchdog = acpi_device->driver_data; + u8 data; + + /* you can only switch boot->user */ + if (strcmp(buf, "user")) + return -EINVAL; + + data = NIWD_CONTROL_MODE | NIWD_CONTROL_RESET; + + outb(data, niwatchdog->io_base + NIWD_CONTROL); + + return count; +} + +static DEVICE_ATTR(watchdog_mode, S_IRUSR|S_IWUSR, niwatchdog_wdmode_get, + niwatchdog_wdmode_set); + +static ssize_t niwatchdog_register_dump_get(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct acpi_device *acpi_device = to_acpi_device(dev); + struct niwatchdog *niwatchdog = acpi_device->driver_data; + u8 control, counter2, counter1, counter0; + u8 seed2, seed1, seed0; + + control = inb(niwatchdog->io_base + NIWD_CONTROL); + counter2 = inb(niwatchdog->io_base + NIWD_COUNTER2); + counter1 = inb(niwatchdog->io_base + NIWD_COUNTER1); + counter0 = inb(niwatchdog->io_base + NIWD_COUNTER0); + seed2 = inb(niwatchdog->io_base + NIWD_SEED2); + seed1 = inb(niwatchdog->io_base + NIWD_SEED1); + seed0 = inb(niwatchdog->io_base + NIWD_SEED0); + + return sprintf(buf, + "Control: 0x%02X\n" + "Counter 2: 0x%02X\n" + "Counter 1: 0x%02X\n" + "Counter 0: 0x%02X\n" + "Seed 2: 0x%02X\n" + "Seed 1: 0x%02X\n" + "Seed 0: 0x%02X\n", + control, counter2, counter1, counter0, + seed2, seed1, seed0); +} + +static DEVICE_ATTR(register_dump, S_IRUGO, niwatchdog_register_dump_get, NULL); + +static const struct attribute *niwatchdog_attrs[] = { + &dev_attr_watchdog_mode.attr, + &dev_attr_register_dump.attr, + NULL +}; + +static acpi_status niwatchdog_resources(struct acpi_resource *res, void *data) +{ + struct niwatchdog *niwatchdog = data; + + switch (res->type) { + case ACPI_RESOURCE_TYPE_IO: + if ((niwatchdog->io_base != 0) || + (niwatchdog->io_size != 0)) { + dev_err(&niwatchdog->acpi_device->dev, + "too many IO resources\n"); + return AE_ERROR; + } + + niwatchdog->io_base = res->data.io.minimum; + niwatchdog->io_size = res->data.io.address_length; + + return AE_OK; + + case ACPI_RESOURCE_TYPE_IRQ: + if (niwatchdog->irq != 0) { + dev_err(&niwatchdog->acpi_device->dev, + "too many IRQ resources\n"); + return AE_ERROR; + } + + niwatchdog->irq = res->data.irq.interrupts[0]; + + return AE_OK; + + case ACPI_RESOURCE_TYPE_END_TAG: + return AE_OK; + + default: + dev_err(&niwatchdog->acpi_device->dev, + "unsupported resource type %d\n", + res->type); + return AE_ERROR; + } + + return AE_OK; +} + +static int niwatchdog_acpi_remove(struct acpi_device *device) +{ + struct niwatchdog *niwatchdog = device->driver_data; + + sysfs_remove_files(&niwatchdog->acpi_device->dev.kobj, + niwatchdog_attrs); + + if ((niwatchdog->io_base != 0) && + (niwatchdog->io_size == NIWD_IO_SIZE)) + release_region(niwatchdog->io_base, niwatchdog->io_size); + + device->driver_data = NULL; + + kfree(niwatchdog); + + return 0; +} + +static int niwatchdog_acpi_add(struct acpi_device *device) +{ + struct niwatchdog *niwatchdog; + acpi_status acpi_ret; + int err; + + niwatchdog = kzalloc(sizeof(*niwatchdog), GFP_KERNEL); + + if (!niwatchdog) + return -ENOMEM; + + device->driver_data = niwatchdog; + + niwatchdog->acpi_device = device; + + acpi_ret = acpi_walk_resources(device->handle, METHOD_NAME__CRS, + niwatchdog_resources, niwatchdog); + + if (ACPI_FAILURE(acpi_ret) || + (niwatchdog->io_base == 0) || + (niwatchdog->io_size != NIWD_IO_SIZE) || + (niwatchdog->irq == 0)) { + niwatchdog_acpi_remove(device); + return -ENODEV; + } + + if (!request_region(niwatchdog->io_base, niwatchdog->io_size, + MODULE_NAME)) { + niwatchdog_acpi_remove(device); + return -EBUSY; + } + + err = sysfs_create_files(&niwatchdog->acpi_device->dev.kobj, + niwatchdog_attrs); + if (err) { + niwatchdog_acpi_remove(device); + return err; + } + + spin_lock_init(&niwatchdog->lock); + + dev_info(&niwatchdog->acpi_device->dev, + "IO range 0x%04X-0x%04X, IRQ %d\n", + niwatchdog->io_base, + niwatchdog->io_base + niwatchdog->io_size - 1, niwatchdog->irq); + + return 0; +} + +static const struct acpi_device_id niwatchdog_device_ids[] = { + {"NIC775C", 0}, + {"", 0}, +}; + +static struct acpi_driver niwatchdog_acpi_driver = { + .name = MODULE_NAME, + .ids = niwatchdog_device_ids, + .ops = { + .add = niwatchdog_acpi_add, + .remove = niwatchdog_acpi_remove, + }, +}; + +static int __init niwatchdog_init(void) +{ + return acpi_bus_register_driver(&niwatchdog_acpi_driver); +} + +static void __exit niwatchdog_exit(void) +{ + acpi_bus_unregister_driver(&niwatchdog_acpi_driver); +} + +module_init(niwatchdog_init); +module_exit(niwatchdog_exit); + +MODULE_DEVICE_TABLE(acpi, niwatchdog_device_ids); +MODULE_DESCRIPTION("NI Watchdog"); +MODULE_AUTHOR("Jeff Westfahl "); +MODULE_LICENSE("GPL"); From b05c0c3b992c1f0b361a3f4e115eed93baf03e22 Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Wed, 5 Mar 2014 09:50:05 -0600 Subject: [PATCH 32/84] niwatchdog: Added ioctl interface for NI Watchdog Added a new header file with an ioctl interface for NI Watchdog. This file is installed as part of 'make headers_install'. Signed-off-by: Jeff Westfahl [gratian: fix Kbuild conflict, no need to explicitly list headers anymore] Signed-off-by: Gratian Crisan --- include/uapi/linux/niwatchdog.h | 39 +++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 include/uapi/linux/niwatchdog.h diff --git a/include/uapi/linux/niwatchdog.h b/include/uapi/linux/niwatchdog.h new file mode 100644 index 0000000000000..804d45f0243fa --- /dev/null +++ b/include/uapi/linux/niwatchdog.h @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2012 National Instruments 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. + * + * 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. + */ + +#ifndef _LINUX_NIWATCHDOG_H_ +#define _LINUX_NIWATCHDOG_H_ + +#include +#include + +#define NIWATCHDOG_ACTION_INTERRUPT 0 +#define NIWATCHDOG_ACTION_RESET 1 + +#define NIWATCHDOG_STATE_RUNNING 0 +#define NIWATCHDOG_STATE_EXPIRED 1 + +#define NIWATCHDOG_IOCTL_PERIOD_NS _IOR('W', 0, __u32) +#define NIWATCHDOG_IOCTL_MAX_COUNTER _IOR('W', 1, __u32) +#define NIWATCHDOG_IOCTL_COUNTER_SET _IOW('W', 2, __u32) +#define NIWATCHDOG_IOCTL_CHECK_ACTION _IOW('W', 3, __u32) +#define NIWATCHDOG_IOCTL_ADD_ACTION _IOW('W', 4, __u32) +#define NIWATCHDOG_IOCTL_START _IO('W', 5) +#define NIWATCHDOG_IOCTL_PET _IOR('W', 6, __u32) +#define NIWATCHDOG_IOCTL_RESET _IO('W', 7) +#define NIWATCHDOG_IOCTL_COUNTER_GET _IOR('W', 8, __u32) + +#define NIWATCHDOG_NAME "niwatchdog" + +#endif /* _LINUX_NIWATCHDOG_H_ */ From 902dedab3226a1a48dcdc1870ff030c6dad5fcbc Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Thu, 6 Mar 2014 11:07:15 -0600 Subject: [PATCH 33/84] niwatchdog: Added NIWATCHDOG_STATE_DISABLED Signed-off-by: Jeff Westfahl --- include/uapi/linux/niwatchdog.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/uapi/linux/niwatchdog.h b/include/uapi/linux/niwatchdog.h index 804d45f0243fa..a5931a4c8a277 100644 --- a/include/uapi/linux/niwatchdog.h +++ b/include/uapi/linux/niwatchdog.h @@ -23,6 +23,7 @@ #define NIWATCHDOG_STATE_RUNNING 0 #define NIWATCHDOG_STATE_EXPIRED 1 +#define NIWATCHDOG_STATE_DISABLED 2 #define NIWATCHDOG_IOCTL_PERIOD_NS _IOR('W', 0, __u32) #define NIWATCHDOG_IOCTL_MAX_COUNTER _IOR('W', 1, __u32) From d5988aaf73d3cf5f5ee1aafd400c8dd3b296950a Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Thu, 6 Mar 2014 11:17:36 -0600 Subject: [PATCH 34/84] niwatchdog: Implemented NI Watchdog for NI Linux x64 targets. Signed-off-by: Jeff Westfahl --- drivers/misc/niwatchdog.c | 363 +++++++++++++++++++++++++++++++++++++- 1 file changed, 361 insertions(+), 2 deletions(-) diff --git a/drivers/misc/niwatchdog.c b/drivers/misc/niwatchdog.c index d488b49e10a5f..f3fe69777d022 100644 --- a/drivers/misc/niwatchdog.c +++ b/drivers/misc/niwatchdog.c @@ -14,6 +14,10 @@ #include #include +#include +#include +#include +#include #define MODULE_NAME "niwatchdog" @@ -27,8 +31,18 @@ #define NIWD_IO_SIZE 0x08 -#define NIWD_CONTROL_MODE 0x80 -#define NIWD_CONTROL_RESET 0x02 +#define NIWD_CONTROL_MODE 0x80 +#define NIWD_CONTROL_PROC_INTERRUPT 0x40 +#define NIWD_CONTROL_PROC_RESET 0x20 +#define NIWD_CONTROL_PET 0x10 +#define NIWD_CONTROL_RUNNING 0x08 +#define NIWD_CONTROL_CAPTURECOUNTER 0x04 +#define NIWD_CONTROL_RESET 0x02 +#define NIWD_CONTROL_ALARM 0x01 + +#define NIWD_PERIOD_NS 30720 +#define NIWD_MAX_COUNTER 0x00FFFFFF + struct niwatchdog { struct acpi_device *acpi_device; @@ -36,6 +50,11 @@ struct niwatchdog { u16 io_size; u32 irq; spinlock_t lock; + struct miscdevice misc_dev; + atomic_t available; + wait_queue_head_t irq_event; + u32 running:1; + u32 expired:1; }; static ssize_t niwatchdog_wdmode_get(struct device *dev, @@ -65,6 +84,12 @@ static ssize_t niwatchdog_wdmode_set(struct device *dev, if (strcmp(buf, "user")) return -EINVAL; + data = inb(niwatchdog->io_base + NIWD_CONTROL); + + /* Check if we're already in user mode. */ + if (!(data & NIWD_CONTROL_MODE)) + return count; + data = NIWD_CONTROL_MODE | NIWD_CONTROL_RESET; outb(data, niwatchdog->io_base + NIWD_CONTROL); @@ -112,6 +137,323 @@ static const struct attribute *niwatchdog_attrs[] = { NULL }; +static int niwatchdog_counter_set(struct niwatchdog *niwatchdog, u32 counter) +{ + int ret; + unsigned long flags; + + spin_lock_irqsave(&niwatchdog->lock, flags); + + /* Prevent changing the counter while the watchdog is running. */ + if (niwatchdog->running) { + ret = -EBUSY; + goto out_unlock; + } + + outb(((0x00FF0000 & counter) >> 16), niwatchdog->io_base + NIWD_SEED2); + outb(((0x0000FF00 & counter) >> 8), niwatchdog->io_base + NIWD_SEED1); + outb((0x000000FF & counter), niwatchdog->io_base + NIWD_SEED0); + + ret = 0; + +out_unlock: + spin_unlock_irqrestore(&niwatchdog->lock, flags); + + return ret; +} + +static int niwatchdog_check_action(u32 action) +{ + int err = 0; + + switch (action) { + case NIWD_CONTROL_PROC_INTERRUPT: + case NIWD_CONTROL_PROC_RESET: + break; + default: + err = -ENOTSUPP; + } + + return err; +} + +static int niwatchdog_add_action(struct niwatchdog *niwatchdog, u32 action) +{ + u8 action_mask; + u8 control; + unsigned long flags; + + if (action == NIWATCHDOG_ACTION_INTERRUPT) + action_mask = NIWD_CONTROL_PROC_INTERRUPT; + else if (action == NIWATCHDOG_ACTION_RESET) + action_mask = NIWD_CONTROL_PROC_RESET; + else + return -ENOTSUPP; + + spin_lock_irqsave(&niwatchdog->lock, flags); + + control = inb(niwatchdog->io_base + NIWD_CONTROL); + control |= action_mask; + outb(control, niwatchdog->io_base + NIWD_CONTROL); + + spin_unlock_irqrestore(&niwatchdog->lock, flags); + + return 0; +} + +static int niwatchdog_start(struct niwatchdog *niwatchdog) +{ + u8 control; + unsigned long flags; + + spin_lock_irqsave(&niwatchdog->lock, flags); + + niwatchdog->running = true; + niwatchdog->expired = false; + + control = inb(niwatchdog->io_base + NIWD_CONTROL); + outb(control | NIWD_CONTROL_RESET, niwatchdog->io_base + NIWD_CONTROL); + outb(control | NIWD_CONTROL_PET, niwatchdog->io_base + NIWD_CONTROL); + + spin_unlock_irqrestore(&niwatchdog->lock, flags); + + return 0; +} + +static int niwatchdog_pet(struct niwatchdog *niwatchdog, u32 *state) +{ + u8 control; + unsigned long flags; + + spin_lock_irqsave(&niwatchdog->lock, flags); + + if (niwatchdog->expired) { + *state = NIWATCHDOG_STATE_EXPIRED; + } else if (!niwatchdog->running) { + *state = NIWATCHDOG_STATE_DISABLED; + } else { + control = inb(niwatchdog->io_base + NIWD_CONTROL); + control |= NIWD_CONTROL_PET; + outb(control, niwatchdog->io_base + NIWD_CONTROL); + + *state = NIWATCHDOG_STATE_RUNNING; + } + + spin_unlock_irqrestore(&niwatchdog->lock, flags); + + return 0; +} + +static int niwatchdog_reset(struct niwatchdog *niwatchdog) +{ + unsigned long flags; + + spin_lock_irqsave(&niwatchdog->lock, flags); + + niwatchdog->running = false; + niwatchdog->expired = false; + + outb(NIWD_CONTROL_RESET, niwatchdog->io_base + NIWD_CONTROL); + + spin_unlock_irqrestore(&niwatchdog->lock, flags); + + return 0; +} + +static int niwatchdog_counter_get(struct niwatchdog *niwatchdog, + u32 *counter) +{ + u8 control; + u8 counter2, counter1, counter0; + unsigned long flags; + + spin_lock_irqsave(&niwatchdog->lock, flags); + + control = inb(niwatchdog->io_base + NIWD_CONTROL); + control |= NIWD_CONTROL_CAPTURECOUNTER; + outb(control, niwatchdog->io_base + NIWD_CONTROL); + + counter2 = inb(niwatchdog->io_base + NIWD_COUNTER2); + counter1 = inb(niwatchdog->io_base + NIWD_COUNTER1); + counter0 = inb(niwatchdog->io_base + NIWD_COUNTER0); + + *counter = (counter2 << 16) | (counter1 << 8) | counter0; + + spin_unlock_irqrestore(&niwatchdog->lock, flags); + + return 0; +} + +static irqreturn_t niwatchdog_irq(int irq, void *data) +{ + struct niwatchdog *niwatchdog = data; + irqreturn_t ret = IRQ_NONE; + u8 control; + unsigned long flags; + + spin_lock_irqsave(&niwatchdog->lock, flags); + + control = inb(niwatchdog->io_base + NIWD_CONTROL); + + if (!(NIWD_CONTROL_ALARM & control)) { + dev_err(&niwatchdog->acpi_device->dev, + "Spurious watchdog interrupt, 0x%02X\n", control); + goto out_unlock; + } + + niwatchdog->running = false; + niwatchdog->expired = true; + + /* Acknowledge the interrupt. */ + control |= NIWD_CONTROL_RESET; + outb(control, niwatchdog->io_base + NIWD_CONTROL); + + /* Signal the watchdog event. */ + wake_up_all(&niwatchdog->irq_event); + + ret = IRQ_HANDLED; + +out_unlock: + spin_unlock_irqrestore(&niwatchdog->lock, flags); + + return ret; +} + +static int niwatchdog_misc_open(struct inode *inode, struct file *file) +{ + struct miscdevice *misc_dev = file->private_data; + struct niwatchdog *niwatchdog = container_of( + misc_dev, struct niwatchdog, misc_dev); + + file->private_data = niwatchdog; + + if (!atomic_dec_and_test(&niwatchdog->available)) { + atomic_inc(&niwatchdog->available); + return -EBUSY; + } + + return request_irq(niwatchdog->irq, niwatchdog_irq, 0, + NIWATCHDOG_NAME, niwatchdog); +} + +static int niwatchdog_misc_release(struct inode *inode, struct file *file) +{ + struct niwatchdog *niwatchdog = file->private_data; + + free_irq(niwatchdog->irq, niwatchdog); + atomic_inc(&niwatchdog->available); + return 0; +} + +static long niwatchdog_misc_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct niwatchdog *niwatchdog = file->private_data; + int err; + + switch (cmd) { + case NIWATCHDOG_IOCTL_PERIOD_NS: { + __u32 period = NIWD_PERIOD_NS; + + err = copy_to_user((__u32 *)arg, &period, + sizeof(__u32)); + break; + } + case NIWATCHDOG_IOCTL_MAX_COUNTER: { + __u32 counter = NIWD_MAX_COUNTER; + + err = copy_to_user((__u32 *)arg, &counter, + sizeof(__u32)); + break; + } + case NIWATCHDOG_IOCTL_COUNTER_SET: { + __u32 counter; + + err = copy_from_user(&counter, (__u32 *)arg, + sizeof(__u32)); + if (!err) + err = niwatchdog_counter_set(niwatchdog, counter); + break; + } + case NIWATCHDOG_IOCTL_CHECK_ACTION: { + __u32 action; + + err = copy_from_user(&action, (__u32 *)arg, + sizeof(__u32)); + if (!err) + err = niwatchdog_check_action(action); + break; + } + case NIWATCHDOG_IOCTL_ADD_ACTION: { + __u32 action; + err = copy_from_user(&action, (__u32 *)arg, + sizeof(__u32)); + if (!err) + err = niwatchdog_add_action(niwatchdog, action); + break; + } + case NIWATCHDOG_IOCTL_START: { + err = niwatchdog_start(niwatchdog); + break; + } + case NIWATCHDOG_IOCTL_PET: { + __u32 state; + + err = niwatchdog_pet(niwatchdog, &state); + if (!err) + err = copy_to_user((__u32 *)arg, &state, + sizeof(__u32)); + break; + } + case NIWATCHDOG_IOCTL_RESET: { + err = niwatchdog_reset(niwatchdog); + break; + } + case NIWATCHDOG_IOCTL_COUNTER_GET: { + __u32 counter; + + err = niwatchdog_counter_get(niwatchdog, &counter); + if (!err) { + err = copy_to_user((__u32 *)arg, &counter, + sizeof(__u32)); + } + break; + } + default: + err = -EINVAL; + break; + }; + + return err; +} + +unsigned int niwatchdog_misc_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct niwatchdog *niwatchdog = file->private_data; + unsigned int mask = 0; + unsigned long flags; + + poll_wait(file, &niwatchdog->irq_event, wait); + + spin_lock_irqsave(&niwatchdog->lock, flags); + + if (niwatchdog->expired) + mask = POLLIN; + + spin_unlock_irqrestore(&niwatchdog->lock, flags); + + return mask; +} + +static const struct file_operations niwatchdog_misc_fops = { + .owner = THIS_MODULE, + .open = niwatchdog_misc_open, + .release = niwatchdog_misc_release, + .unlocked_ioctl = niwatchdog_misc_ioctl, + .poll = niwatchdog_misc_poll, +}; + static acpi_status niwatchdog_resources(struct acpi_resource *res, void *data) { struct niwatchdog *niwatchdog = data; @@ -158,6 +500,8 @@ static int niwatchdog_acpi_remove(struct acpi_device *device) { struct niwatchdog *niwatchdog = device->driver_data; + misc_deregister(&niwatchdog->misc_dev); + sysfs_remove_files(&niwatchdog->acpi_device->dev.kobj, niwatchdog_attrs); @@ -213,6 +557,21 @@ static int niwatchdog_acpi_add(struct acpi_device *device) spin_lock_init(&niwatchdog->lock); + atomic_set(&niwatchdog->available, 1); + init_waitqueue_head(&niwatchdog->irq_event); + niwatchdog->expired = false; + + niwatchdog->misc_dev.minor = MISC_DYNAMIC_MINOR; + niwatchdog->misc_dev.name = NIWATCHDOG_NAME; + niwatchdog->misc_dev.fops = &niwatchdog_misc_fops; + + err = misc_register(&niwatchdog->misc_dev); + + if (err) { + niwatchdog_acpi_remove(device); + return err; + } + dev_info(&niwatchdog->acpi_device->dev, "IO range 0x%04X-0x%04X, IRQ %d\n", niwatchdog->io_base, From a1844000d65a59c21abf466d8ec1ec5bdb7c2846 Mon Sep 17 00:00:00 2001 From: Jeff Westfahl Date: Thu, 10 Apr 2014 16:40:22 -0500 Subject: [PATCH 35/84] niwatchdog: remove register_dump Removed the register_dump sysfs file. The watchdog works well, there's no longer a need for this debugging file. Signed-off-by: Jeff Westfahl --- drivers/misc/niwatchdog.c | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/drivers/misc/niwatchdog.c b/drivers/misc/niwatchdog.c index f3fe69777d022..c09702b6c19a8 100644 --- a/drivers/misc/niwatchdog.c +++ b/drivers/misc/niwatchdog.c @@ -100,40 +100,8 @@ static ssize_t niwatchdog_wdmode_set(struct device *dev, static DEVICE_ATTR(watchdog_mode, S_IRUSR|S_IWUSR, niwatchdog_wdmode_get, niwatchdog_wdmode_set); -static ssize_t niwatchdog_register_dump_get(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct acpi_device *acpi_device = to_acpi_device(dev); - struct niwatchdog *niwatchdog = acpi_device->driver_data; - u8 control, counter2, counter1, counter0; - u8 seed2, seed1, seed0; - - control = inb(niwatchdog->io_base + NIWD_CONTROL); - counter2 = inb(niwatchdog->io_base + NIWD_COUNTER2); - counter1 = inb(niwatchdog->io_base + NIWD_COUNTER1); - counter0 = inb(niwatchdog->io_base + NIWD_COUNTER0); - seed2 = inb(niwatchdog->io_base + NIWD_SEED2); - seed1 = inb(niwatchdog->io_base + NIWD_SEED1); - seed0 = inb(niwatchdog->io_base + NIWD_SEED0); - - return sprintf(buf, - "Control: 0x%02X\n" - "Counter 2: 0x%02X\n" - "Counter 1: 0x%02X\n" - "Counter 0: 0x%02X\n" - "Seed 2: 0x%02X\n" - "Seed 1: 0x%02X\n" - "Seed 0: 0x%02X\n", - control, counter2, counter1, counter0, - seed2, seed1, seed0); -} - -static DEVICE_ATTR(register_dump, S_IRUGO, niwatchdog_register_dump_get, NULL); - static const struct attribute *niwatchdog_attrs[] = { &dev_attr_watchdog_mode.attr, - &dev_attr_register_dump.attr, NULL }; From 5e9d9d1812d76873e61b58a6ecc338f12fcf5aa1 Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Fri, 19 Feb 2016 14:27:22 -0600 Subject: [PATCH 36/84] niwatchdog: Explicitly request threaded interrupt Requesting a threaded interrupt explicity and doing so with a NULL handler/non-NULL thread_fn avoids disabling softirqs while the handler executes. This does require setting IRQF_ONESHOT, which was set automatically when using request_irq before. Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: Josh Cartwright Natinst-ReviewBoard-ID: 127047 --- drivers/misc/niwatchdog.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/niwatchdog.c b/drivers/misc/niwatchdog.c index c09702b6c19a8..1b9ba115b94b9 100644 --- a/drivers/misc/niwatchdog.c +++ b/drivers/misc/niwatchdog.c @@ -300,8 +300,8 @@ static int niwatchdog_misc_open(struct inode *inode, struct file *file) return -EBUSY; } - return request_irq(niwatchdog->irq, niwatchdog_irq, 0, - NIWATCHDOG_NAME, niwatchdog); + return request_threaded_irq(niwatchdog->irq, NULL, niwatchdog_irq, + IRQF_ONESHOT, NIWATCHDOG_NAME, niwatchdog); } static int niwatchdog_misc_release(struct inode *inode, struct file *file) From e21cb3c1eb3b5cfff2f7da19b87621aca4fd4ec3 Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Mon, 7 Aug 2017 15:17:44 -0500 Subject: [PATCH 37/84] niwatchdog: Fix error path in ACPI add Use device-managed resource allocation where possible and clean up correctly in the event of an error during ACPI add. Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: Zach Brown Acked-by: James Minor Acked-by: Ovidiu Vancea Acked-by: Julia Cartwright Natinst-ReviewBoard-ID: 196333 --- drivers/misc/niwatchdog.c | 94 +++++++++++++-------------------------- 1 file changed, 32 insertions(+), 62 deletions(-) diff --git a/drivers/misc/niwatchdog.c b/drivers/misc/niwatchdog.c index 1b9ba115b94b9..83a6a8bd75691 100644 --- a/drivers/misc/niwatchdog.c +++ b/drivers/misc/niwatchdog.c @@ -47,7 +47,6 @@ struct niwatchdog { struct acpi_device *acpi_device; u16 io_base; - u16 io_size; u32 irq; spinlock_t lock; struct miscdevice misc_dev; @@ -425,25 +424,35 @@ static const struct file_operations niwatchdog_misc_fops = { static acpi_status niwatchdog_resources(struct acpi_resource *res, void *data) { struct niwatchdog *niwatchdog = data; + struct device *dev = &niwatchdog->acpi_device->dev; + u16 io_size; switch (res->type) { case ACPI_RESOURCE_TYPE_IO: - if ((niwatchdog->io_base != 0) || - (niwatchdog->io_size != 0)) { - dev_err(&niwatchdog->acpi_device->dev, - "too many IO resources\n"); + if (niwatchdog->io_base != 0) { + dev_err(dev, "too many IO resources\n"); return AE_ERROR; } niwatchdog->io_base = res->data.io.minimum; - niwatchdog->io_size = res->data.io.address_length; + io_size = res->data.io.address_length; + + if (io_size < NIWD_IO_SIZE) { + dev_err(dev, "memory region too small\n"); + return AE_ERROR; + } + + if (!devm_request_region(dev, niwatchdog->io_base, io_size, + MODULE_NAME)) { + dev_err(dev, "failed to get memory region\n"); + return AE_ERROR; + } return AE_OK; case ACPI_RESOURCE_TYPE_IRQ: if (niwatchdog->irq != 0) { - dev_err(&niwatchdog->acpi_device->dev, - "too many IRQ resources\n"); + dev_err(dev, "too many IRQ resources\n"); return AE_ERROR; } @@ -455,13 +464,9 @@ static acpi_status niwatchdog_resources(struct acpi_resource *res, void *data) return AE_OK; default: - dev_err(&niwatchdog->acpi_device->dev, - "unsupported resource type %d\n", - res->type); + dev_err(dev, "unsupported resource type %d\n", res->type); return AE_ERROR; } - - return AE_OK; } static int niwatchdog_acpi_remove(struct acpi_device *device) @@ -473,78 +478,54 @@ static int niwatchdog_acpi_remove(struct acpi_device *device) sysfs_remove_files(&niwatchdog->acpi_device->dev.kobj, niwatchdog_attrs); - if ((niwatchdog->io_base != 0) && - (niwatchdog->io_size == NIWD_IO_SIZE)) - release_region(niwatchdog->io_base, niwatchdog->io_size); - - device->driver_data = NULL; - - kfree(niwatchdog); - return 0; } static int niwatchdog_acpi_add(struct acpi_device *device) { + struct device *dev = &device->dev; struct niwatchdog *niwatchdog; acpi_status acpi_ret; int err; - niwatchdog = kzalloc(sizeof(*niwatchdog), GFP_KERNEL); - + niwatchdog = devm_kzalloc(dev, sizeof(*niwatchdog), GFP_KERNEL); if (!niwatchdog) return -ENOMEM; device->driver_data = niwatchdog; - niwatchdog->acpi_device = device; acpi_ret = acpi_walk_resources(device->handle, METHOD_NAME__CRS, niwatchdog_resources, niwatchdog); - - if (ACPI_FAILURE(acpi_ret) || - (niwatchdog->io_base == 0) || - (niwatchdog->io_size != NIWD_IO_SIZE) || - (niwatchdog->irq == 0)) { - niwatchdog_acpi_remove(device); + if (ACPI_FAILURE(acpi_ret) || niwatchdog->io_base == 0 || + niwatchdog->irq == 0) { + dev_err(dev, "failed to get resources\n"); return -ENODEV; } - if (!request_region(niwatchdog->io_base, niwatchdog->io_size, - MODULE_NAME)) { - niwatchdog_acpi_remove(device); - return -EBUSY; - } - - err = sysfs_create_files(&niwatchdog->acpi_device->dev.kobj, - niwatchdog_attrs); - if (err) { - niwatchdog_acpi_remove(device); - return err; - } - spin_lock_init(&niwatchdog->lock); atomic_set(&niwatchdog->available, 1); init_waitqueue_head(&niwatchdog->irq_event); niwatchdog->expired = false; + err = sysfs_create_files(&dev->kobj, niwatchdog_attrs); + if (err) { + dev_err(dev, "failed to create sysfs attributes\n"); + return err; + } + niwatchdog->misc_dev.minor = MISC_DYNAMIC_MINOR; niwatchdog->misc_dev.name = NIWATCHDOG_NAME; niwatchdog->misc_dev.fops = &niwatchdog_misc_fops; err = misc_register(&niwatchdog->misc_dev); - if (err) { - niwatchdog_acpi_remove(device); + dev_err(dev, "failed to register misc device\n"); + sysfs_remove_files(&dev->kobj, niwatchdog_attrs); return err; } - dev_info(&niwatchdog->acpi_device->dev, - "IO range 0x%04X-0x%04X, IRQ %d\n", - niwatchdog->io_base, - niwatchdog->io_base + niwatchdog->io_size - 1, niwatchdog->irq); - return 0; } @@ -562,18 +543,7 @@ static struct acpi_driver niwatchdog_acpi_driver = { }, }; -static int __init niwatchdog_init(void) -{ - return acpi_bus_register_driver(&niwatchdog_acpi_driver); -} - -static void __exit niwatchdog_exit(void) -{ - acpi_bus_unregister_driver(&niwatchdog_acpi_driver); -} - -module_init(niwatchdog_init); -module_exit(niwatchdog_exit); +module_acpi_driver(niwatchdog_acpi_driver); MODULE_DEVICE_TABLE(acpi, niwatchdog_device_ids); MODULE_DESCRIPTION("NI Watchdog"); From bdc9b47ff06bcf75eeb97cd0b833978f499eeb2e Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Wed, 2 Aug 2017 13:08:03 -0500 Subject: [PATCH 38/84] niwatchdog: Request IRQ on ACPI add instead of miscdevice open Do what most sane drivers do and request our IRQ on ACPI add rather than doing it every time someone opens our char device. This ensures that our thread exists at boot time, which means priority changes can be done in user mode. Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: Zach Brown Acked-by: James Minor Acked-by: Ovidiu Vancea Acked-by: Julia Cartwright Natinst-CAR-ID: 648185 Natinst-ReviewBoard-ID: 196333 --- drivers/misc/niwatchdog.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/drivers/misc/niwatchdog.c b/drivers/misc/niwatchdog.c index 83a6a8bd75691..df7f5cfc7b855 100644 --- a/drivers/misc/niwatchdog.c +++ b/drivers/misc/niwatchdog.c @@ -299,15 +299,13 @@ static int niwatchdog_misc_open(struct inode *inode, struct file *file) return -EBUSY; } - return request_threaded_irq(niwatchdog->irq, NULL, niwatchdog_irq, - IRQF_ONESHOT, NIWATCHDOG_NAME, niwatchdog); + return 0; } static int niwatchdog_misc_release(struct inode *inode, struct file *file) { struct niwatchdog *niwatchdog = file->private_data; - free_irq(niwatchdog->irq, niwatchdog); atomic_inc(&niwatchdog->available); return 0; } @@ -458,6 +456,13 @@ static acpi_status niwatchdog_resources(struct acpi_resource *res, void *data) niwatchdog->irq = res->data.irq.interrupts[0]; + if (devm_request_threaded_irq(dev, niwatchdog->irq, NULL, + niwatchdog_irq, IRQF_ONESHOT, + NIWATCHDOG_NAME, niwatchdog)) { + dev_err(dev, "failed to get interrupt\n"); + return AE_ERROR; + } + return AE_OK; case ACPI_RESOURCE_TYPE_END_TAG: From 31c660e2f20634906bdf9462b995c6beee4f692e Mon Sep 17 00:00:00 2001 From: Hui Chun Ong Date: Mon, 6 Feb 2017 23:49:09 +0800 Subject: [PATCH 39/84] watchdog: nic7018_wdt: Add support for trigger pet and trigger assert Add capabilities to use trigger line to reset timer and to assert trigger line upon timeout. The trigger line control and selection is done through sysfs attributes. Signed-off-by: Hui Chun Ong Signed-off-by: Brad Mouring Acked-by: Gratian Crisan Acked-by: Julia Cartwright Natinst-ReviewBoard-ID: 192326 (cherry picked from commit d6351b1a5530fb532e948fec6a8e947cca384880) (cherry picked from commit be8a29797de3c2fc60a44993edc24071da2c7f5f) --- drivers/watchdog/nic7018_wdt.c | 168 +++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/drivers/watchdog/nic7018_wdt.c b/drivers/watchdog/nic7018_wdt.c index 2a46cc6629437..b5cb8a4d4e151 100644 --- a/drivers/watchdog/nic7018_wdt.c +++ b/drivers/watchdog/nic7018_wdt.c @@ -8,7 +8,9 @@ #include #include #include +#include #include +#include #include #define LOCK 0xA5 @@ -16,6 +18,8 @@ #define WDT_CTRL_RESET_EN BIT(7) #define WDT_RELOAD_PORT_EN BIT(7) +#define WDT_CTRL_TRIG_POL BIT(4) +#define WDT_RELOAD_TRIG_POL BIT(6) #define WDT_CTRL 1 #define WDT_RELOAD_CTRL 2 @@ -46,6 +50,7 @@ struct nic7018_wdt { u16 io_base; u32 period; struct watchdog_device wdd; + struct mutex lock; }; struct nic7018_config { @@ -165,6 +170,166 @@ static const struct watchdog_ops nic7018_wdd_ops = { .get_timeleft = nic7018_get_timeleft, }; +struct nic7018_attr { + struct device_attribute dev_attr; + u8 offset; + u8 bit; +}; +#define to_nic7018_attr(_attr) \ + container_of((_attr), struct nic7018_attr, dev_attr) + +static ssize_t wdt_attr_show(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); + struct nic7018_attr *attr = to_nic7018_attr(da); + u8 control; + + mutex_lock(&wdt->lock); + + control = inb(wdt->io_base + attr->offset); + + mutex_unlock(&wdt->lock); + return sprintf(buf, "%u\n", !!(control & attr->bit)); +} + +static ssize_t wdt_attr_store(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t size) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); + struct nic7018_attr *attr = to_nic7018_attr(da); + unsigned long val; + u8 control; + + int ret = kstrtoul(buf, 10, &val); + + if (ret) + return ret; + + if (val > 1) + return -EINVAL; + + mutex_lock(&wdt->lock); + + control = inb(wdt->io_base + attr->offset); + if (val) + outb(control | attr->bit, wdt->io_base + attr->offset); + else + outb(control & ~attr->bit, wdt->io_base + attr->offset); + + mutex_unlock(&wdt->lock); + return size; +} + +#define WDT_ATTR(_name, _offset, _bit) \ + struct nic7018_attr dev_attr_##_name = { \ + .offset = _offset, \ + .bit = _bit, \ + .dev_attr = \ + __ATTR(_name, S_IWUSR | S_IRUGO, \ + wdt_attr_show, wdt_attr_store), \ + } + +static WDT_ATTR(enable_reset, WDT_CTRL, WDT_CTRL_RESET_EN); +static WDT_ATTR(enable_soft_ping, WDT_RELOAD_CTRL, WDT_RELOAD_PORT_EN); +static WDT_ATTR(trigger_polarity, WDT_CTRL, WDT_CTRL_TRIG_POL); +static WDT_ATTR(keepalive_trigger_polarity, WDT_RELOAD_CTRL, + WDT_RELOAD_TRIG_POL); + +static ssize_t wdt_trig_show(struct device *dev, + struct device_attribute *da, + char *buf) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); + struct nic7018_attr *attr = to_nic7018_attr(da); + u8 control; + + mutex_lock(&wdt->lock); + + control = inb(wdt->io_base + attr->offset); + + mutex_unlock(&wdt->lock); + + if (control & 0x0F) + return sprintf(buf, "trig%u\n", (control & 0x0F) - 1); + else + return sprintf(buf, "none\n"); +} + +static ssize_t wdt_trig_store(struct device *dev, + struct device_attribute *da, + const char *buf, + size_t size) +{ + struct watchdog_device *wdd = dev_get_drvdata(dev); + struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); + struct nic7018_attr *attr = to_nic7018_attr(da); + u8 control; + + char *p = memchr(buf, '\n', size); + size_t count = p ? p - buf : size; + + if (count == 5 && !strncmp(buf, "trig", 4)) { + unsigned long val; + int ret; + + ret = kstrtoul(buf + 4, 10, &val); + if (ret) + return ret; + + if (val > 8) + return -EINVAL; + + mutex_lock(&wdt->lock); + + control = inb(wdt->io_base + attr->offset); + outb((control & 0xF0) | (val + 1), + wdt->io_base + attr->offset); + + mutex_unlock(&wdt->lock); + + } else if (count == 4 && !strncmp(buf, "none", 4)) { + mutex_lock(&wdt->lock); + + control = inb(wdt->io_base + attr->offset); + outb(control & 0xF0, wdt->io_base + attr->offset); + + mutex_unlock(&wdt->lock); + } else { + return -EINVAL; + } + + return size; +} + +#define WDT_TRIG_ATTR(_name, _offset) \ + struct nic7018_attr dev_attr_##_name = { \ + .offset = _offset, \ + .dev_attr = \ + __ATTR(_name, S_IWUSR | S_IRUGO, \ + wdt_trig_show, wdt_trig_store), \ + } + +static WDT_TRIG_ATTR(trigger, WDT_CTRL); +static WDT_TRIG_ATTR(keepalive_trigger, WDT_RELOAD_CTRL); + +static struct attribute *nic7018_wdt_attrs[] = { + &dev_attr_enable_reset.dev_attr.attr, + &dev_attr_enable_soft_ping.dev_attr.attr, + &dev_attr_trigger_polarity.dev_attr.attr, + &dev_attr_keepalive_trigger_polarity.dev_attr.attr, + &dev_attr_trigger.dev_attr.attr, + &dev_attr_keepalive_trigger.dev_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(nic7018_wdt); + static int nic7018_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -191,6 +356,8 @@ static int nic7018_probe(struct platform_device *pdev) return -EBUSY; } + mutex_init(&wdt->lock); + wdt->io_base = io_rc->start; wdd = &wdt->wdd; wdd->info = &nic7018_wdd_info; @@ -199,6 +366,7 @@ static int nic7018_probe(struct platform_device *pdev) wdd->max_timeout = WDT_MAX_TIMEOUT; wdd->timeout = WDT_DEFAULT_TIMEOUT; wdd->parent = dev; + wdd->groups = nic7018_wdt_groups; watchdog_set_drvdata(wdd, wdt); watchdog_set_nowayout(wdd, nowayout); From ee36efdeb792d026ddf9a6950ececf5c05873985 Mon Sep 17 00:00:00 2001 From: Hui Chun Ong Date: Sat, 8 Jul 2017 12:33:34 +0800 Subject: [PATCH 40/84] watchdog: nic7018_wdt: Add support for timeout interrupt Add interrupt handling for watchdog timeout. Interrupt control is done through sysfs "enable_interrupt" attribute. uevent is used to signal userspace when interrupt occurred as oppose to using poll due to no support in watchdog core. Signed-off-by: Hui Chun Ong Signed-off-by: Brad Mouring Acked-by: Julia Cartwright Natinst-ReviewBoard-ID: 195479 --- drivers/watchdog/nic7018_wdt.c | 45 +++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/drivers/watchdog/nic7018_wdt.c b/drivers/watchdog/nic7018_wdt.c index b5cb8a4d4e151..30c9bccc685fe 100644 --- a/drivers/watchdog/nic7018_wdt.c +++ b/drivers/watchdog/nic7018_wdt.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -20,7 +21,9 @@ #define WDT_RELOAD_PORT_EN BIT(7) #define WDT_CTRL_TRIG_POL BIT(4) #define WDT_RELOAD_TRIG_POL BIT(6) +#define WDT_CTRL_INTERRUPT_EN BIT(5) +#define WDT_STATUS 0 #define WDT_CTRL 1 #define WDT_RELOAD_CTRL 2 #define WDT_PRESET_PRESCALE 4 @@ -106,6 +109,31 @@ static int nic7018_set_timeout(struct watchdog_device *wdd, return 0; } +static irqreturn_t nic7018_thread_isr(int irq, void *wdt_arg) +{ + struct nic7018_wdt *wdt = wdt_arg; + struct watchdog_device *wdd = &wdt->wdd; + u8 status, control; + + status = inb(wdt->io_base + WDT_STATUS); + + /* IRQ line asserted */ + if (status & 0x20) { + + mutex_lock(&wdt->lock); + + control = inb(wdt->io_base + WDT_CTRL); + /* Disable IRQ line */ + outb(control & ~WDT_CTRL_INTERRUPT_EN, + wdt->io_base + WDT_CTRL); + + mutex_unlock(&wdt->lock); + + kobject_uevent(&wdd->parent->kobj, KOBJ_CHANGE); + } + return IRQ_HANDLED; +} + static int nic7018_start(struct watchdog_device *wdd) { struct nic7018_wdt *wdt = watchdog_get_drvdata(wdd); @@ -240,6 +268,7 @@ static WDT_ATTR(enable_soft_ping, WDT_RELOAD_CTRL, WDT_RELOAD_PORT_EN); static WDT_ATTR(trigger_polarity, WDT_CTRL, WDT_CTRL_TRIG_POL); static WDT_ATTR(keepalive_trigger_polarity, WDT_RELOAD_CTRL, WDT_RELOAD_TRIG_POL); +static WDT_ATTR(enable_interrupt, WDT_CTRL, WDT_CTRL_INTERRUPT_EN); static ssize_t wdt_trig_show(struct device *dev, struct device_attribute *da, @@ -324,6 +353,7 @@ static struct attribute *nic7018_wdt_attrs[] = { &dev_attr_enable_soft_ping.dev_attr.attr, &dev_attr_trigger_polarity.dev_attr.attr, &dev_attr_keepalive_trigger_polarity.dev_attr.attr, + &dev_attr_enable_interrupt.dev_attr.attr, &dev_attr_trigger.dev_attr.attr, &dev_attr_keepalive_trigger.dev_attr.attr, NULL @@ -336,7 +366,7 @@ static int nic7018_probe(struct platform_device *pdev) struct watchdog_device *wdd; struct nic7018_wdt *wdt; struct resource *io_rc; - int ret; + int ret, irq; wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); if (!wdt) @@ -356,6 +386,10 @@ static int nic7018_probe(struct platform_device *pdev) return -EBUSY; } + irq = platform_get_irq(pdev, 0); + if (!irq) + return -EINVAL; + mutex_init(&wdt->lock); wdt->io_base = io_rc->start; @@ -372,6 +406,15 @@ static int nic7018_probe(struct platform_device *pdev) watchdog_set_nowayout(wdd, nowayout); watchdog_init_timeout(wdd, timeout, dev); + ret = devm_request_threaded_irq(dev, irq, NULL, + nic7018_thread_isr, + IRQF_ONESHOT, + KBUILD_MODNAME, wdt); + if (ret) { + dev_err(dev, "failed to register interrupt handler\n"); + return ret; + } + /* Unlock WDT register */ outb(UNLOCK, wdt->io_base + WDT_REG_LOCK); From bd1a0f95629750aaba9e2122035e39e678e314bd Mon Sep 17 00:00:00 2001 From: Jaeden Amero Date: Thu, 2 Aug 2012 15:51:01 -0500 Subject: [PATCH 41/84] serial_core: Provide a transceiver interface Before this change, to enable transceiver control for RS-485 ports, client drivers had to implement the TIOCSRS485/TIOCGRS485 ioctls. This change provides a common transceiver interface so that we have a common way for all client drivers to do transceiver control. When registering a new port, client drivers will have the option of providing a struct txvr_ops which has callbacks for transceiver operations. This makes it easier to have a common UART driver (like the 8250) for both RS-232 and RS-485 ports and transceiver operations can be added for the RS-485 ports alone. Signed-off-by: Jaeden Amero Signed-off-by: Xander Huff Acked-by: Karthik Manamcheri Acked-by: Ben Shelton Natinst-ReviewBoard-ID: 88262 --- drivers/tty/serial/serial_core.c | 11 +++++++++++ include/linux/serial_core.h | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c index 0e2e35ab64c79..b54e3552f076f 100644 --- a/drivers/tty/serial/serial_core.c +++ b/drivers/tty/serial/serial_core.c @@ -229,6 +229,10 @@ static int uart_port_startup(struct tty_struct *tty, struct uart_state *state, */ uart_change_speed(tty, state, NULL); + /* Enable Transceivers */ + if (uport->txvr_ops && uport->txvr_ops->enable_transceivers) + uport->txvr_ops->enable_transceivers(uport); + /* * Setup the RTS and DTR signals once the * port is open and ready to respond. @@ -1669,6 +1673,10 @@ static void uart_port_shutdown(struct tty_port *port) struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = uart_port_check(state); + /* Disable Transceivers */ + if (uport->txvr_ops && uport->txvr_ops->disable_transceivers) + uport->txvr_ops->disable_transceivers(uport); + /* * clear delta_msr_wait queue to avoid mem leaks: we may free * the irq here so the queue might never be woken up. Note @@ -2260,6 +2268,9 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *uport) if (tty) uart_change_speed(tty, state, NULL); spin_lock_irq(&uport->lock); + if (uport->rs485_config) + uport->rs485_config(uport, + &uport->rs485); ops->set_mctrl(uport, uport->mctrl); ops->start_tx(uport); spin_unlock_irq(&uport->lock); diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index c58cc142d23f4..25a5d5d427372 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -82,6 +82,15 @@ struct uart_ops { #endif }; +/* + * This structure describes all the operations that can be done through + * transceiver controllers. + */ +struct txvr_ops { + int (*enable_transceivers)(struct uart_port *); + int (*disable_transceivers)(struct uart_port *); +}; + #define NO_POLL_CHAR 0x00ff0000 #define UART_CONFIG_TYPE (1 << 0) #define UART_CONFIG_IRQ (1 << 1) @@ -234,6 +243,7 @@ struct uart_port { unsigned int timeout; /* character-based timeout */ unsigned int type; /* port type */ const struct uart_ops *ops; + const struct txvr_ops *txvr_ops; /* transceiver ops */ unsigned int custom_divisor; unsigned int line; /* port index */ unsigned int minor; From 126be3c50f4bd3f2837c7dff7494bd86e239ad37 Mon Sep 17 00:00:00 2001 From: Jaeden Amero Date: Tue, 11 Mar 2014 15:43:01 -0500 Subject: [PATCH 42/84] 8250: Allow client drivers to control transceivers This change allows 8250 client drivers to register txvr_ops, so that they can implement transceiver control for RS-485 ports. Signed-off-by: Jaeden Amero Signed-off-by: Xander Huff Acked-by: Karthik Manamcheri Acked-by: Ben Shelton Natinst-ReviewBoard-ID: 88262 --- drivers/tty/serial/8250/8250_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index fad00c0414e34..fab15a462928d 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1055,6 +1055,8 @@ int serial8250_register_8250_port(const struct uart_8250_port *up) uart->port.serial_out = up->port.serial_out; if (up->port.handle_irq) uart->port.handle_irq = up->port.handle_irq; + if (up->port.txvr_ops) + uart->port.txvr_ops = up->port.txvr_ops; /* Possibly override set_termios call */ if (up->port.set_termios) uart->port.set_termios = up->port.set_termios; From 65f86640d9eb0a5c6bdf33a3caa415ca7dc6cbdd Mon Sep 17 00:00:00 2001 From: Jaeden Amero Date: Tue, 11 Mar 2014 16:52:15 -0500 Subject: [PATCH 43/84] 8250: Add a driver for NI 16550 UART The National Instruments (NI) 16550 is a standard 16550 with larger FIFOs and embedded RS-485 transceiver control circuitry. This patch adds a driver that can operate this UART. Signed-off-by: Jaeden Amero Acked-by: Karthik Manamcheri [gratian: fix conflict with aa42db44f670 ("8250: of: remove remnants of generic of_serial driver")] Signed-off-by: Gratian Crisan [bstreiff: renumber out-of-tree definition of PORT_NI16550 from 118 to 123.] Signed-off-by: Brandon Streiff --- drivers/tty/serial/8250/8250_ni16550.c | 134 +++++++++++++++++++++++++ drivers/tty/serial/8250/8250_of.c | 24 +++++ drivers/tty/serial/8250/Kconfig | 6 ++ drivers/tty/serial/8250/Makefile | 1 + include/linux/ni16550.h | 26 +++++ include/uapi/linux/serial_core.h | 3 + include/uapi/linux/serial_reg.h | 1 + 7 files changed, 195 insertions(+) create mode 100644 drivers/tty/serial/8250/8250_ni16550.c create mode 100644 include/linux/ni16550.h diff --git a/drivers/tty/serial/8250/8250_ni16550.c b/drivers/tty/serial/8250/8250_ni16550.c new file mode 100644 index 0000000000000..d8dc0960fc3b2 --- /dev/null +++ b/drivers/tty/serial/8250/8250_ni16550.c @@ -0,0 +1,134 @@ +/* + * NI 16550 Transceiver Driver + * + * The National Instruments (NI) 16550 has built-in RS-485 transceiver control + * circuitry. This driver provides the transceiver control functionality + * for the RS-485 ports and uses the 8250 driver for the UART functionality. + * + * Copyright 2012 National Instruments Corporation + * + * 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 "8250.h" +#include +#include +#include + +#define NI16550_PCR_OFFSET 0x0F +#define NI16550_PCR_RS422 0x00 +#define NI16550_PCR_ECHO_RS485 0x01 +#define NI16550_PCR_DTR_RS485 0x02 +#define NI16550_PCR_AUTO_RS485 0x03 +#define NI16550_PCR_WIRE_MODE_MASK 0x03 +#define NI16550_PCR_TXVR_ENABLE_BIT (1 << 3) +#define NI16550_PCR_RS485_TERMINATION_BIT (1 << 6) + +static int ni16550_enable_transceivers(struct uart_port *port) +{ + uint8_t pcr; + + dev_dbg(port->dev, ">ni16550_enable_transceivers\n"); + + pcr = port->serial_in(port, NI16550_PCR_OFFSET); + pcr |= NI16550_PCR_TXVR_ENABLE_BIT; + dev_dbg(port->dev, "write pcr: 0x%08x\n", pcr); + port->serial_out(port, NI16550_PCR_OFFSET, pcr); + + dev_dbg(port->dev, "dev, ">ni16550_disable_transceivers\n"); + + pcr = port->serial_in(port, NI16550_PCR_OFFSET); + pcr &= ~NI16550_PCR_TXVR_ENABLE_BIT; + dev_dbg(port->dev, "write pcr: 0x%08x\n", pcr); + port->serial_out(port, NI16550_PCR_OFFSET, pcr); + + dev_dbg(port->dev, "dev, ">ni16550_config_rs485\n"); + + /* "rs485" should be given to us non-NULL. */ + BUG_ON(rs485 == NULL); + + pcr = port->serial_in(port, NI16550_PCR_OFFSET); + pcr &= ~NI16550_PCR_WIRE_MODE_MASK; + + if (rs485->flags & SER_RS485_ENABLED) { + /* RS-485 */ + if ((rs485->flags & SER_RS485_RX_DURING_TX) && + (rs485->flags & SER_RS485_RTS_ON_SEND)) { + dev_dbg(port->dev, "Invalid 2-wire mode\n"); + return -EINVAL; + } + + if (rs485->flags & SER_RS485_RX_DURING_TX) { + /* Echo */ + dev_vdbg(port->dev, "2-wire DTR with echo\n"); + pcr |= NI16550_PCR_ECHO_RS485; + } else { + /* Auto or DTR */ + if (rs485->flags & SER_RS485_RTS_ON_SEND) { + /* Auto */ + dev_vdbg(port->dev, "2-wire Auto\n"); + pcr |= NI16550_PCR_AUTO_RS485; + } else { + /* DTR-controlled */ + /* No Echo */ + dev_vdbg(port->dev, "2-wire DTR no echo\n"); + pcr |= NI16550_PCR_DTR_RS485; + } + } + } else { + /* RS-422 */ + dev_vdbg(port->dev, "4-wire\n"); + pcr |= NI16550_PCR_RS422; + } + + dev_dbg(port->dev, "write pcr: 0x%08x\n", pcr); + port->serial_out(port, NI16550_PCR_OFFSET, pcr); + + /* Update the cache. */ + port->rs485 = *rs485; + + dev_dbg(port->dev, "txvr_ops = &ni16550_txvr_ops; + port->rs485_config = &ni16550_config_rs485; + /* The hardware comes up by default in 2-wire auto mode and we + * set the flags to represent that + */ + port->rs485.flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND; +} diff --git a/drivers/tty/serial/8250/8250_of.c b/drivers/tty/serial/8250/8250_of.c index bce28729dd7bd..e05153cca2850 100644 --- a/drivers/tty/serial/8250/8250_of.c +++ b/drivers/tty/serial/8250/8250_of.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -163,6 +164,23 @@ static int of_platform_serial_setup(struct platform_device *ofdev, case PORT_RT2880: port->iotype = UPIO_AU; break; +#ifdef CONFIG_SERIAL_8250_NI16550 + case PORT_NI16550_F16: + case PORT_NI16550_F128: + { + const char *transceiver; + + if (of_property_read_string(np, "transceiver", &transceiver)) { + dev_warn(&ofdev->dev, "no transceiver property set\n"); + ret = -EINVAL; + goto err_dispose; + } + + if (strcmp(transceiver, "RS-485") == 0) + ni16550_port_setup(port); + break; + } +#endif } if (IS_ENABLED(CONFIG_SERIAL_8250_FSL) && @@ -324,6 +342,12 @@ static const struct of_device_id of_platform_serial_table[] = { { .compatible = "ti,da830-uart", .data = (void *)PORT_DA830, }, { .compatible = "nuvoton,wpcm450-uart", .data = (void *)PORT_NPCM, }, { .compatible = "nuvoton,npcm750-uart", .data = (void *)PORT_NPCM, }, +#ifdef CONFIG_SERIAL_8250_NI16550 + { .compatible = "ni16550-fifo16", + .data = (void *)PORT_NI16550_F16, }, + { .compatible = "ni16550-fifo128", + .data = (void *)PORT_NI16550_F128, }, +#endif { /* end of list */ }, }; MODULE_DEVICE_TABLE(of, of_platform_serial_table); diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig index 39fc96dc2531c..3388868a7552a 100644 --- a/drivers/tty/serial/8250/Kconfig +++ b/drivers/tty/serial/8250/Kconfig @@ -535,3 +535,9 @@ config SERIAL_OF_PLATFORM are probed through devicetree, including Open Firmware based PowerPC systems and embedded systems on architectures using the flattened device tree format. + +config SERIAL_8250_NI16550 + bool "NI 16550 UART support" + depends on SERIAL_8250 + help + This driver supports the National Instruments (NI) 16550 UART port. diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile index b9bcd73c89975..8825482f7191c 100644 --- a/drivers/tty/serial/8250/Makefile +++ b/drivers/tty/serial/8250/Makefile @@ -40,5 +40,6 @@ obj-$(CONFIG_SERIAL_8250_PXA) += 8250_pxa.o obj-$(CONFIG_SERIAL_8250_TEGRA) += 8250_tegra.o obj-$(CONFIG_SERIAL_8250_BCM7271) += 8250_bcm7271.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += 8250_of.o +obj-$(CONFIG_SERIAL_8250_NI16550) += 8250_ni16550.o CFLAGS_8250_ingenic.o += -I$(srctree)/scripts/dtc/libfdt diff --git a/include/linux/ni16550.h b/include/linux/ni16550.h new file mode 100644 index 0000000000000..eea3857551d4a --- /dev/null +++ b/include/linux/ni16550.h @@ -0,0 +1,26 @@ +/* + * NI 16550 Transceiver Driver + * + * The National Instruments (NI) 16550 has built-in RS-485 transceiver control + * circuitry. This driver provides the transceiver control functionality + * for the RS-485 ports and uses the 8250 driver for the UART functionality. + * + * Copyright 2012 National Instruments Corporation + * + * 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. + */ + +#ifndef _NI16550_H +#define _NI16550_H + +void ni16550_port_setup(struct uart_port *port); + +#endif diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index c4042dcfdc0c3..420ad04750ef8 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -274,4 +274,7 @@ /* Freescale LINFlexD UART */ #define PORT_LINFLEXUART 122 +/* National Instruments 16550 UART */ +#define PORT_NI16550 123 + #endif /* _UAPILINUX_SERIAL_CORE_H */ diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index f51bc8f368134..f65f9a60b002c 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -64,6 +64,7 @@ * TI16C752: 8 16 56 60 8 16 32 56 * OX16C950: 16 32 112 120 16 32 64 112 PORT_16C950 * Tegra: 1 4 8 14 16 8 4 1 PORT_TEGRA + * NI 16550: 1 32 64 112 xx xx xx xx PORT_NI16550 */ #define UART_FCR_R_TRIG_00 0x00 #define UART_FCR_R_TRIG_01 0x40 From bbd852f73ff96d73ce235c681edf315e473fa13a Mon Sep 17 00:00:00 2001 From: Karthik Manamcheri Date: Wed, 12 Mar 2014 13:09:29 -0500 Subject: [PATCH 44/84] 8250_pnp: Make NIC7750 ID spawn a NI 16550 port This commit adds a new ACPI ID, NIC7750, which will be used to represent a National Instruments NI 16550 UART. To enable this driver, you need to set CONFIG_SERIAL_8250_NI16550 in the kernel config. Signed-off-by: Karthik Manamcheri Acked-by: Jaeden Amero --- drivers/tty/serial/8250/8250_pnp.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 98e5ee4d0d08a..e34ecbde9b8c7 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -21,9 +21,21 @@ #include "8250.h" +#include + #define UNKNOWN_DEV 0x3000 #define CIR_PORT 0x0800 +#define NI_PORT_ID "NIC7750" +#define NI_PORT_CLK 33333333 +static bool is_niport(struct pnp_dev *dev) +{ + if (strncmp(dev->id->id, NI_PORT_ID, sizeof(NI_PORT_ID)) == 0) + return true; + else + return false; +} + static const struct pnp_device_id pnp_dev_table[] = { /* Archtek America Corp. */ /* Archtek SmartLink Modem 3334BT Plug & Play */ @@ -195,6 +207,8 @@ static const struct pnp_device_id pnp_dev_table[] = { /* Com 1 */ /* Deskline K56 Phone System PnP */ { "MVX00A1", 0 }, + /* National Instruments (NI) 16550 PNP */ + { NI_PORT_ID, 0 }, /* PC Rider K56 Phone System PnP */ { "MVX00F2", 0 }, /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ @@ -479,6 +493,14 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) device_property_read_u32(&dev->dev, "clock-frequency", &uart.port.uartclk); uart.port.dev = &dev->dev; +#ifdef CONFIG_SERIAL_8250_NI16550 + if (is_niport(dev)) { + uart.port.uartclk = NI_PORT_CLK; + uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE; + uart.port.type = PORT_NI16550; + ni16550_port_setup(&uart.port); + } +#endif line = serial8250_register_8250_port(&uart); if (line < 0 || (flags & CIR_PORT)) return -ENODEV; From 63f6dd9714498da85ff8031ad45944e61c83ad0e Mon Sep 17 00:00:00 2001 From: Karthik Manamcheri Date: Thu, 22 May 2014 15:29:15 -0500 Subject: [PATCH 45/84] 8250_pnp: NI ACPI IDs start with NIC To determine if a device is a NI device, we should check the first three characters of the ACPI ID (All NI IDs start with "NIC"). Signed-off-by: Karthik Manamcheri Acked-by: Jaeden Amero --- drivers/tty/serial/8250/8250_pnp.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index e34ecbde9b8c7..b04fc8c38aadf 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -26,11 +26,12 @@ #define UNKNOWN_DEV 0x3000 #define CIR_PORT 0x0800 -#define NI_PORT_ID "NIC7750" -#define NI_PORT_CLK 33333333 +#define NI_CLK_33333333 0x0002 + static bool is_niport(struct pnp_dev *dev) { - if (strncmp(dev->id->id, NI_PORT_ID, sizeof(NI_PORT_ID)) == 0) + /* All National Instruments ACPI IDs start with NIC */ + if (strncmp(dev->id->id, "NIC", 3) == 0) return true; else return false; @@ -207,10 +208,10 @@ static const struct pnp_device_id pnp_dev_table[] = { /* Com 1 */ /* Deskline K56 Phone System PnP */ { "MVX00A1", 0 }, - /* National Instruments (NI) 16550 PNP */ - { NI_PORT_ID, 0 }, /* PC Rider K56 Phone System PnP */ { "MVX00F2", 0 }, + /* National Instruments (NI) 16550 PNP */ + { "NIC7750", NI_CLK_33333333 }, /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ { "nEC8241", 0 }, /* Pace 56 Voice Internal Plug & Play Modem */ @@ -495,7 +496,9 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) #ifdef CONFIG_SERIAL_8250_NI16550 if (is_niport(dev)) { - uart.port.uartclk = NI_PORT_CLK; + if (flags & NI_CLK_33333333) + uart.port.uartclk = 33333333; + uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE; uart.port.type = PORT_NI16550; ni16550_port_setup(&uart.port); From a2e00b1e0818cb672bcddb01715c185fe46b9f7f Mon Sep 17 00:00:00 2001 From: Karthik Manamcheri Date: Thu, 22 May 2014 15:50:09 -0500 Subject: [PATCH 46/84] 8250_pnp: Support dual-mode UART through PMR The NI CVS-1459RT Controller has a NI 16550 UART which can be connected to either a RS-232 transceiver or a RS-485 transceiver. This dual-mode support is implemented through the Port Mode Register (PMR). This change will add support for the 8250 PNP driver to read the PMR and based on the mode, will register the appropriate driver. Signed-off-by: Karthik Manamcheri Acked-by: Jaeden Amero Acked-by: Justin Tang --- drivers/tty/serial/8250/8250_ni16550.c | 50 ++++++++++++++++++++++++++ drivers/tty/serial/8250/8250_pnp.c | 19 ++++++++-- include/linux/ni16550.h | 1 + 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/drivers/tty/serial/8250/8250_ni16550.c b/drivers/tty/serial/8250/8250_ni16550.c index d8dc0960fc3b2..5c886836ae056 100644 --- a/drivers/tty/serial/8250/8250_ni16550.c +++ b/drivers/tty/serial/8250/8250_ni16550.c @@ -32,6 +32,33 @@ #define NI16550_PCR_TXVR_ENABLE_BIT (1 << 3) #define NI16550_PCR_RS485_TERMINATION_BIT (1 << 6) +#define NI16550_PMR_OFFSET 0x0E +/* + * PMR[1:0] - Port Capabilities + * + * 0 - Register not implemented/supported + * 1 - RS-232 capable + * 2 - RS-485 capable + * 3 - RS-232/RS-485 dual-mode capable + * + */ +#define NI16550_PMR_CAP_MASK 0x03 +#define NI16550_PMR_NOT_IMPL 0x00 +#define NI16550_PMR_CAP_RS232 0x01 +#define NI16550_PMR_CAP_RS485 0x02 +#define NI16550_PMR_CAP_DUAL 0x03 +/* + * PMR[4] - Interface Mode + * + * 0 - RS-232 mode + * 1 - RS-485 mode + * + */ +#define NI16550_PMR_MODE_MASK 0x10 +#define NI16550_PMR_MODE_RS232 0x00 +#define NI16550_PMR_MODE_RS485 0x10 + + static int ni16550_enable_transceivers(struct uart_port *port) { uint8_t pcr; @@ -118,6 +145,29 @@ static int ni16550_config_rs485(struct uart_port *port, return 0; } +bool is_rs232_mode(unsigned long iobase) +{ + uint8_t pmr = inb(iobase + NI16550_PMR_OFFSET); + + /* If the PMR is not implemented, then by default NI UARTs are + * connected to RS-485 transceivers + */ + if ((pmr & NI16550_PMR_CAP_MASK) == NI16550_PMR_NOT_IMPL) + return false; + + if ((pmr & NI16550_PMR_CAP_MASK) == NI16550_PMR_CAP_DUAL) + /* If the port is dual-mode capable, then read the mode bit + * to know the current mode + */ + return ((pmr & NI16550_PMR_MODE_MASK) + == NI16550_PMR_MODE_RS232); + else + /* If it is not dual-mode capable, then decide based on the + * capability + */ + return ((pmr & NI16550_PMR_CAP_MASK) == NI16550_PMR_CAP_RS232); +} + static struct txvr_ops ni16550_txvr_ops = { .enable_transceivers = ni16550_enable_transceivers, .disable_transceivers = ni16550_disable_transceivers, diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index b04fc8c38aadf..879e0c7337e35 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -27,6 +27,7 @@ #define CIR_PORT 0x0800 #define NI_CLK_33333333 0x0002 +#define NI_CAP_PMR 0x0001 static bool is_niport(struct pnp_dev *dev) { @@ -498,10 +499,24 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) if (is_niport(dev)) { if (flags & NI_CLK_33333333) uart.port.uartclk = 33333333; - uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE; uart.port.type = PORT_NI16550; - ni16550_port_setup(&uart.port); + + /* + * NI UARTs are by default connected to RS-485 transceivers, + * unless the PMR register is implemented, and the UART is + * dual-mode capable. Then it could be in RS-232 mode and if it + * is, we will register as a standard 8250 port. + */ + if ((flags & NI_CAP_PMR) && is_rs232_mode(uart.port.iobase)) + pr_info("NI 16550 at I/O 0x%x (irq = %d) is dual-mode capable and is in RS-232 mode\n", + (unsigned int)uart.port.iobase, + uart.port.irq); + else + /* Either the PMR register is not implemented, or it is + * and the UART is in RS-485 mode as set in the PMR + */ + ni16550_port_setup(&uart.port); } #endif line = serial8250_register_8250_port(&uart); diff --git a/include/linux/ni16550.h b/include/linux/ni16550.h index eea3857551d4a..a71d71c27ae44 100644 --- a/include/linux/ni16550.h +++ b/include/linux/ni16550.h @@ -21,6 +21,7 @@ #ifndef _NI16550_H #define _NI16550_H +bool is_rs232_mode(unsigned long iobase); void ni16550_port_setup(struct uart_port *port); #endif From cee9f5d2b620e1379d1f41d9f7751dac211b318e Mon Sep 17 00:00:00 2001 From: Karthik Manamcheri Date: Fri, 23 May 2014 10:00:22 -0500 Subject: [PATCH 47/84] 8250: NI 16550 can have different FIFO sizes The NI 16550 is a CPLD UART and depending on restrictions on CPLD size, different implementations can have different FIFO sizes. This change provides two NI 16550 types, one with a smaller 16-byte FIFOs and the other with the original 128-byte FIFOs. Signed-off-by: Karthik Manamcheri Acked-by: Justin Tang Acked-by: Jaeden Amero [bstreiff: renumber PORT_NI16550_*] Signed-off-by: Brandon Streiff --- drivers/tty/serial/8250/8250_pnp.c | 2 +- drivers/tty/serial/8250/8250_port.c | 14 ++++++++++++++ include/uapi/linux/serial_core.h | 3 ++- include/uapi/linux/serial_reg.h | 3 ++- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 879e0c7337e35..5c0956065e13d 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -500,7 +500,7 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) if (flags & NI_CLK_33333333) uart.port.uartclk = 33333333; uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE; - uart.port.type = PORT_NI16550; + uart.port.type = PORT_NI16550_F128; /* * NI UARTs are by default connected to RS-485 transceivers, diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 9371521fccb31..ed20404c00ef1 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -307,6 +307,20 @@ static const struct serial8250_config uart_config[] = { .rxtrig_bytes = {1, 32, 64, 112}, .flags = UART_CAP_FIFO | UART_CAP_SLEEP, }, + [PORT_NI16550_F16] = { + .name = "NI 16550 (with 16-byte FIFOs)", + .fifo_size = 16, + .tx_loadsz = 16, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR, + }, + [PORT_NI16550_F128] = { + .name = "NI 16550 (with 128-byte FIFOs)", + .fifo_size = 128, + .tx_loadsz = 128, + .fcr = UART_FCR_ENABLE_FIFO | UART_FCR_R_TRIG_10, + .flags = UART_CAP_FIFO | UART_CAP_AFE | UART_CAP_EFR, + }, }; /* Uart divisor latch read */ diff --git a/include/uapi/linux/serial_core.h b/include/uapi/linux/serial_core.h index 420ad04750ef8..e6d4d961d5416 100644 --- a/include/uapi/linux/serial_core.h +++ b/include/uapi/linux/serial_core.h @@ -275,6 +275,7 @@ #define PORT_LINFLEXUART 122 /* National Instruments 16550 UART */ -#define PORT_NI16550 123 +#define PORT_NI16550_F16 123 /* 16-byte FIFOs */ +#define PORT_NI16550_F128 124 /* 128-byte FIFOs */ #endif /* _UAPILINUX_SERIAL_CORE_H */ diff --git a/include/uapi/linux/serial_reg.h b/include/uapi/linux/serial_reg.h index f65f9a60b002c..2ed8f8b2eb0b7 100644 --- a/include/uapi/linux/serial_reg.h +++ b/include/uapi/linux/serial_reg.h @@ -64,7 +64,8 @@ * TI16C752: 8 16 56 60 8 16 32 56 * OX16C950: 16 32 112 120 16 32 64 112 PORT_16C950 * Tegra: 1 4 8 14 16 8 4 1 PORT_TEGRA - * NI 16550: 1 32 64 112 xx xx xx xx PORT_NI16550 + * NI 16550: 1 4 8 14 xx xx xx xx PORT_NI16550_F16 + * NI 16550: 1 32 64 112 xx xx xx xx PORT_NI16550_F128 */ #define UART_FCR_R_TRIG_00 0x00 #define UART_FCR_R_TRIG_01 0x40 From e3c89ae837d9f5c6ddbb4890b4effddc4467cced Mon Sep 17 00:00:00 2001 From: Karthik Manamcheri Date: Fri, 23 May 2014 14:46:42 -0500 Subject: [PATCH 48/84] 8250_pnp: Provide NI_16BYTE_FIFO flag This change will provide a flag, NI_16BYTE_FIFO, which when used as the flag for ACPI IDs will spawn a NI UART with a 16-byte FIFO (instead of the default 128-byte FIFO) Signed-off-by: Karthik Manamcheri Acked-by: Justin Tang --- drivers/tty/serial/8250/8250_pnp.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 5c0956065e13d..a56e39699b62b 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -26,6 +26,7 @@ #define UNKNOWN_DEV 0x3000 #define CIR_PORT 0x0800 +#define NI_16BYTE_FIFO 0x0004 #define NI_CLK_33333333 0x0002 #define NI_CAP_PMR 0x0001 @@ -500,7 +501,11 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) if (flags & NI_CLK_33333333) uart.port.uartclk = 33333333; uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE; - uart.port.type = PORT_NI16550_F128; + + if (flags & NI_16BYTE_FIFO) + uart.port.type = PORT_NI16550_F16; + else + uart.port.type = PORT_NI16550_F128; /* * NI UARTs are by default connected to RS-485 transceivers, From 784c40691779401723d66639247c9c815a4fae74 Mon Sep 17 00:00:00 2001 From: Karthik Manamcheri Date: Fri, 23 May 2014 15:59:54 -0500 Subject: [PATCH 49/84] 8250_pnp: Make NIC7772 spawn a dual-mode UART This commit adds a new ACPI ID, NIC7772. This ID represents a National Instruments NI 16550 UART which is dual-mode capable (Port Mode Register is implemented) and has 16-byte FIFOs. To use this port, you need to set CONFIG_SERIAL_8250_NI16550 in the kernel config. Signed-off-by: Karthik Manamcheri --- drivers/tty/serial/8250/8250_pnp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index a56e39699b62b..03a66ed7b2921 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -213,7 +213,8 @@ static const struct pnp_device_id pnp_dev_table[] = { /* PC Rider K56 Phone System PnP */ { "MVX00F2", 0 }, /* National Instruments (NI) 16550 PNP */ - { "NIC7750", NI_CLK_33333333 }, + { "NIC7750", NI_CLK_33333333 }, + { "NIC7772", NI_CAP_PMR | NI_16BYTE_FIFO }, /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ { "nEC8241", 0 }, /* Pace 56 Voice Internal Plug & Play Modem */ From 92b9724bede2b52841e47a824ffa7bbac39f3344 Mon Sep 17 00:00:00 2001 From: Nathan Sullivan Date: Tue, 26 Apr 2016 15:59:36 -0500 Subject: [PATCH 50/84] ACPI / PNP: Add IDs for NI 16550 to list Add the two PNP IDs for NI 16550 serial ports to the PNP device ID list, so they are enumerated as PNP devices. Signed-off-by: Nathan Sullivan Signed-off-by: Brad Mouring Acked-by: Xander Huff Natinst-ReviewBoard-ID: 135498 Natinst-CAR-ID: 584586 --- drivers/acpi/acpi_pnp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c index 8f2dc176bb412..43d0effdde898 100644 --- a/drivers/acpi/acpi_pnp.c +++ b/drivers/acpi/acpi_pnp.c @@ -220,6 +220,8 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = { {"MVX00A1"}, /* Deskline K56 Phone System PnP */ {"MVX00F2"}, /* PC Rider K56 Phone System PnP */ {"nEC8241"}, /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ + {"NIC7750"}, /* National Instruments (NI) 16550 PNP */ + {"NIC7772"}, /* National Instruments (NI) 16550 PNP */ {"PMC2430"}, /* Pace 56 Voice Internal Plug & Play Modem */ {"PNP0500"}, /* Generic standard PC COM port */ {"PNP0501"}, /* Generic 16550A-compatible COM port */ From 5bde87fa78aba1126daa5a4a3f7bd9115364c213 Mon Sep 17 00:00:00 2001 From: Stephen Lee Date: Fri, 23 Dec 2016 00:26:24 -0600 Subject: [PATCH 51/84] 8250_pnp: Add new ID NIC792B for NI 16550 UART 25MHz Add the new ACPI ID NIC792B into 8250_pnp driver to support NI 16550 UART with 25MHz clock. Signed-off-by: Stephen Lee Signed-off-by: Brad Mouring Acked-by: Hui Chun Ong Reviewed-by: Jason Smith Natinst-ReviewBoard-ID: 166881 --- drivers/acpi/acpi_pnp.c | 1 + drivers/tty/serial/8250/8250_core.c | 1 + drivers/tty/serial/8250/8250_ni16550.c | 25 +++++++++++++++++++++++++ drivers/tty/serial/8250/8250_pnp.c | 20 ++++++++++++++++---- include/linux/ni16550.h | 1 + 5 files changed, 44 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c index 43d0effdde898..7d9ab63624396 100644 --- a/drivers/acpi/acpi_pnp.c +++ b/drivers/acpi/acpi_pnp.c @@ -222,6 +222,7 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = { {"nEC8241"}, /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ {"NIC7750"}, /* National Instruments (NI) 16550 PNP */ {"NIC7772"}, /* National Instruments (NI) 16550 PNP */ + {"NIC792B"}, /* National Instruments (NI) 16550 PNP */ {"PMC2430"}, /* Pace 56 Voice Internal Plug & Play Modem */ {"PNP0500"}, /* Generic standard PC COM port */ {"PNP0501"}, /* Generic 16550A-compatible COM port */ diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index fab15a462928d..28bbb7ffa352a 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -1017,6 +1017,7 @@ int serial8250_register_8250_port(const struct uart_8250_port *up) uart->rs485_start_tx = up->rs485_start_tx; uart->rs485_stop_tx = up->rs485_stop_tx; uart->dma = up->dma; + uart->mcr_force = up->mcr_force; /* Take tx_loadsz from fifosize if it wasn't set separately */ if (uart->port.fifosize && !uart->tx_loadsz) diff --git a/drivers/tty/serial/8250/8250_ni16550.c b/drivers/tty/serial/8250/8250_ni16550.c index 5c886836ae056..a36fdae983ca6 100644 --- a/drivers/tty/serial/8250/8250_ni16550.c +++ b/drivers/tty/serial/8250/8250_ni16550.c @@ -168,6 +168,31 @@ bool is_rs232_mode(unsigned long iobase) return ((pmr & NI16550_PMR_CAP_MASK) == NI16550_PMR_CAP_RS232); } +void ni16550_config_prescaler(unsigned long iobase, uint8_t prescaler) +{ + /* Page in the Enhanced Mode Registers + * Sets EFR[4] for Enhanced Mode. + */ + uint8_t lcr_value; + + lcr_value = inb(iobase + UART_LCR); + outb(UART_LCR_CONF_MODE_B, iobase + UART_LCR); + + uint8_t efr_value; + + efr_value = inb(iobase + UART_EFR); + efr_value |= UART_EFR_ECB; + + outb(efr_value, iobase + UART_EFR); + + /* Page out the Enhanced Mode Registers */ + outb(lcr_value, iobase + UART_LCR); + + /* Set prescaler to CPR register. */ + outb(UART_CPR, iobase + UART_SCR); + outb(prescaler, iobase + UART_ICR); +} + static struct txvr_ops ni16550_txvr_ops = { .enable_transceivers = ni16550_enable_transceivers, .disable_transceivers = ni16550_disable_transceivers, diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 03a66ed7b2921..25c0f3531a609 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -26,9 +26,10 @@ #define UNKNOWN_DEV 0x3000 #define CIR_PORT 0x0800 -#define NI_16BYTE_FIFO 0x0004 -#define NI_CLK_33333333 0x0002 -#define NI_CAP_PMR 0x0001 +#define NI_16BYTE_FIFO 0x0004 +#define NI_CAP_PMR 0x0001 +#define NI_CLK_33333333 0x0002 +#define NI_CPR_CLK_25000000 0x0008 static bool is_niport(struct pnp_dev *dev) { @@ -215,6 +216,7 @@ static const struct pnp_device_id pnp_dev_table[] = { /* National Instruments (NI) 16550 PNP */ { "NIC7750", NI_CLK_33333333 }, { "NIC7772", NI_CAP_PMR | NI_16BYTE_FIFO }, + { "NIC792B", NI_CPR_CLK_25000000 }, /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ { "nEC8241", 0 }, /* Pace 56 Voice Internal Plug & Play Modem */ @@ -499,8 +501,19 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) #ifdef CONFIG_SERIAL_8250_NI16550 if (is_niport(dev)) { + WARN_ON((flags & (NI_CLK_33333333 | NI_CPR_CLK_25000000)) == + (NI_CLK_33333333 | NI_CPR_CLK_25000000)); if (flags & NI_CLK_33333333) uart.port.uartclk = 33333333; + if (flags & NI_CPR_CLK_25000000) { + /* Sets UART clock rate to 22.222 MHz + * with prescaler 1.125. + */ + uart.port.uartclk = 22222222; + uart.mcr_force = UART_MCR_CLKSEL; + ni16550_config_prescaler(uart.port.iobase, 0x9); + } + uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE; if (flags & NI_16BYTE_FIFO) @@ -587,4 +600,3 @@ void serial8250_pnp_exit(void) { pnp_unregister_driver(&serial_pnp_driver); } - diff --git a/include/linux/ni16550.h b/include/linux/ni16550.h index a71d71c27ae44..155277de2f1aa 100644 --- a/include/linux/ni16550.h +++ b/include/linux/ni16550.h @@ -23,5 +23,6 @@ bool is_rs232_mode(unsigned long iobase); void ni16550_port_setup(struct uart_port *port); +void ni16550_config_prescaler(unsigned long iobase, uint8_t prescaler); #endif From 2704672598d8e33fda625a4e60f9b795767595f1 Mon Sep 17 00:00:00 2001 From: Karthik Manamcheri Date: Fri, 14 Mar 2014 13:15:59 -0500 Subject: [PATCH 52/84] 8250: Make SERIAL_8250_RUNTIME_UARTS work correctly Consider a situation where I have an ARM based system (such as the Zynq) and therefore no legacy ports. I have three memory-mapped ports (such as cRIO-9068). I use device tree to describe the ports. What would be the config options I set so that I get only the three ports in my system? I do not want legacy ports being created automatically and I want it to be flexible enough that it creates the devices based only on the device tree. I expected setting SERIAL_8250_RUNTIME_UARTS = 0 to work because the description said, "Set this to the maximum number of serial ports you want the kernel to register at boot time." Unfortunately, even though SERIAL_8250_NR_UARTS was set to the default value of 4, I did not get any device nodes (because SERIAL_8250_RUNTIME_UARTS was 0). This is what this change is addressing. SERIAL_8250_NR_UARTS controls the maximum number of ports you can support. SERIAL_8250_RUNTIME_UARTS specifies the number of ports you want to create automatically for legacy ports at boot time. All other ports will be created when serial8250_register_port is called (and if it does not exceed the total number of supported ports as specified by SERIAL_8250_NR_UARTS). Signed-off-by: Karthik Manamcheri Acked-by: Jaeden Amero --- drivers/tty/serial/8250/8250_core.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 28bbb7ffa352a..ed18f4f6c57d8 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -493,7 +493,7 @@ static void __init serial8250_isa_init_ports(void) if (nr_uarts > UART_NR) nr_uarts = UART_NR; - for (i = 0; i < nr_uarts; i++) { + for (i = 0; i < UART_NR; i++) { struct uart_8250_port *up = &serial8250_ports[i]; struct uart_port *port = &up->port; @@ -592,7 +592,7 @@ static int univ8250_console_setup(struct console *co, char *options) * if so, search for the first available port that does have * console support. */ - if (co->index >= nr_uarts) + if (co->index >= UART_NR) co->index = 0; port = &serial8250_ports[co->index].port; /* link port to console */ @@ -645,7 +645,7 @@ static int univ8250_console_match(struct console *co, char *name, int idx, return -ENODEV; /* try to match the port specified on the command line */ - for (i = 0; i < nr_uarts; i++) { + for (i = 0; i < UART_NR; i++) { struct uart_port *port = &serial8250_ports[i].port; if (port->iotype != iotype) @@ -853,7 +853,7 @@ static int serial8250_remove(struct platform_device *dev) { int i; - for (i = 0; i < nr_uarts; i++) { + for (i = 0; i < UART_NR; i++) { struct uart_8250_port *up = &serial8250_ports[i]; if (up->port.dev == &dev->dev) @@ -920,7 +920,7 @@ static struct uart_8250_port *serial8250_find_match_or_unused(const struct uart_ /* * First, find a port entry which matches. */ - for (i = 0; i < nr_uarts; i++) + for (i = 0; i < UART_NR; i++) if (uart_match_port(&serial8250_ports[i].port, port)) return &serial8250_ports[i]; @@ -934,7 +934,7 @@ static struct uart_8250_port *serial8250_find_match_or_unused(const struct uart_ * free entry. We look for one which hasn't been previously * used (indicated by zero iobase). */ - for (i = 0; i < nr_uarts; i++) + for (i = 0; i < UART_NR; i++) if (serial8250_ports[i].port.type == PORT_UNKNOWN && serial8250_ports[i].port.iobase == 0) return &serial8250_ports[i]; @@ -943,7 +943,7 @@ static struct uart_8250_port *serial8250_find_match_or_unused(const struct uart_ * That also failed. Last resort is to find any entry which * doesn't have a real port associated with it. */ - for (i = 0; i < nr_uarts; i++) + for (i = 0; i < UART_NR; i++) if (serial8250_ports[i].port.type == PORT_UNKNOWN) return &serial8250_ports[i]; From a3e18e001ca305100e2a7b9f5246740112af3a54 Mon Sep 17 00:00:00 2001 From: Richard Tollerton Date: Fri, 21 Apr 2017 14:13:52 -0500 Subject: [PATCH 53/84] Revert "serial: 8250: Do nothing if nr_uarts=0" This reverts commit 59cfc45f17d6d1dda2990e6f5a94df24a18330b8. It conflicts with "8250: Make SERIAL_8250_RUNTIME_UARTS work correctly", and so prevents systems whose UARTs are all hotplugged from being able to set nr_uarts=0 on bootup. Natinst-CAR-ID: 634278 Signed-off-by: Richard Tollerton Signed-off-by: Brad Mouring Natinst-ReviewBoard-ID: 183619 --- drivers/tty/serial/8250/8250_core.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index ed18f4f6c57d8..0a2338115c468 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -680,9 +680,6 @@ static struct console univ8250_console = { static int __init univ8250_console_init(void) { - if (nr_uarts == 0) - return -ENODEV; - serial8250_isa_init_ports(); register_console(&univ8250_console); return 0; @@ -713,7 +710,7 @@ int __init early_serial_setup(struct uart_port *port) { struct uart_port *p; - if (port->line >= ARRAY_SIZE(serial8250_ports) || nr_uarts == 0) + if (port->line >= ARRAY_SIZE(serial8250_ports)) return -ENODEV; serial8250_isa_init_ports(); @@ -1168,9 +1165,6 @@ static int __init serial8250_init(void) { int ret; - if (nr_uarts == 0) - return -ENODEV; - serial8250_isa_init_ports(); pr_info("Serial: 8250/16550 driver, %d ports, IRQ sharing %sabled\n", From 7bf726f5f8ad20357bbf868dab14a3afe24bd4d9 Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Thu, 11 Apr 2019 13:30:59 -0500 Subject: [PATCH 54/84] serial: 8250_ni16550: Add support for NIC7A69 This device uses a 33.333MHz clock with a prescaler value of 1.125 to achieve baud rates within 1% error up to 921600 baud. We query ACPI standard _DSD properties to determine if each port is an RS-232 or RS-485 port. Signed-off-by: Kyle Roeschley Natinst-ReviewBoard-ID: 286745 --- drivers/acpi/acpi_pnp.c | 1 + drivers/tty/serial/8250/8250_pnp.c | 50 ++++++++++++++++++++++++------ 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/drivers/acpi/acpi_pnp.c b/drivers/acpi/acpi_pnp.c index 7d9ab63624396..7db19611e4cc0 100644 --- a/drivers/acpi/acpi_pnp.c +++ b/drivers/acpi/acpi_pnp.c @@ -223,6 +223,7 @@ static const struct acpi_device_id acpi_pnp_device_ids[] = { {"NIC7750"}, /* National Instruments (NI) 16550 PNP */ {"NIC7772"}, /* National Instruments (NI) 16550 PNP */ {"NIC792B"}, /* National Instruments (NI) 16550 PNP */ + {"NIC7A69"}, /* National Instruments (NI) 16550 PNP */ {"PMC2430"}, /* Pace 56 Voice Internal Plug & Play Modem */ {"PNP0500"}, /* Generic standard PC COM port */ {"PNP0501"}, /* Generic 16550A-compatible COM port */ diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c index 25c0f3531a609..186d438cd66ae 100644 --- a/drivers/tty/serial/8250/8250_pnp.c +++ b/drivers/tty/serial/8250/8250_pnp.c @@ -22,6 +22,7 @@ #include "8250.h" #include +#include #define UNKNOWN_DEV 0x3000 #define CIR_PORT 0x0800 @@ -30,6 +31,7 @@ #define NI_CAP_PMR 0x0001 #define NI_CLK_33333333 0x0002 #define NI_CPR_CLK_25000000 0x0008 +#define NI_CPR_CLK_33333333 0x0010 static bool is_niport(struct pnp_dev *dev) { @@ -217,6 +219,7 @@ static const struct pnp_device_id pnp_dev_table[] = { { "NIC7750", NI_CLK_33333333 }, { "NIC7772", NI_CAP_PMR | NI_16BYTE_FIFO }, { "NIC792B", NI_CPR_CLK_25000000 }, + { "NIC7A69", NI_CPR_CLK_33333333 }, /* NEC 98NOTE SPEAKER PHONE FAX MODEM(33600bps) */ { "nEC8241", 0 }, /* Pace 56 Voice Internal Plug & Play Modem */ @@ -501,10 +504,15 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) #ifdef CONFIG_SERIAL_8250_NI16550 if (is_niport(dev)) { - WARN_ON((flags & (NI_CLK_33333333 | NI_CPR_CLK_25000000)) == - (NI_CLK_33333333 | NI_CPR_CLK_25000000)); + bool rs232_property = false; + + WARN_ON(hweight32(flags & (NI_CPR_CLK_25000000 | + NI_CPR_CLK_33333333 | + NI_CLK_33333333)) > 1); + if (flags & NI_CLK_33333333) uart.port.uartclk = 33333333; + if (flags & NI_CPR_CLK_25000000) { /* Sets UART clock rate to 22.222 MHz * with prescaler 1.125. @@ -514,6 +522,27 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) ni16550_config_prescaler(uart.port.iobase, 0x9); } + if (flags & NI_CPR_CLK_33333333) { + const char *transceiver; + + /* + * Set UART clock rate to 29.629 MHz + * with prescaler 1.125. + */ + uart.port.uartclk = 29629629; + uart.mcr_force = UART_MCR_CLKSEL; + ni16550_config_prescaler(uart.port.iobase, 0x9); + + if (device_property_read_string(&dev->dev, + "transceiver", + &transceiver)) { + dev_err(&dev->dev, "transceiver property missing\n"); + return -EINVAL; + } + + rs232_property = strncmp(transceiver, "RS-232", 6) == 0; + } + uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE; if (flags & NI_16BYTE_FIFO) @@ -522,18 +551,21 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) uart.port.type = PORT_NI16550_F128; /* - * NI UARTs are by default connected to RS-485 transceivers, - * unless the PMR register is implemented, and the UART is - * dual-mode capable. Then it could be in RS-232 mode and if it - * is, we will register as a standard 8250 port. + * NI UARTs may be connected to RS-485 or RS-232 transceivers, + * depending on the ACPI 'transceiver' property and whether or + * not the PMR is implemented. If the PMR is implemented and + * the port is in RS-232 mode, register as a standard 8250 port + * and print about it. */ if ((flags & NI_CAP_PMR) && is_rs232_mode(uart.port.iobase)) pr_info("NI 16550 at I/O 0x%x (irq = %d) is dual-mode capable and is in RS-232 mode\n", (unsigned int)uart.port.iobase, uart.port.irq); - else - /* Either the PMR register is not implemented, or it is - * and the UART is in RS-485 mode as set in the PMR + else if (!rs232_property) + /* + * Either the PMR is implemented and set to RS-485 mode + * or it's not implemented and the 'transceiver' ACPI + * property is 'RS-485'; */ ni16550_port_setup(&uart.port); } From c293a5dfcdc6263c2a12595567e0d8ad81b44339 Mon Sep 17 00:00:00 2001 From: Jonathan David Date: Thu, 15 Oct 2015 15:39:04 -0500 Subject: [PATCH 55/84] e1000e: Add delays after writing to registers There is a noticeable impact on determinism when a large number of writes are flushed. Writes to the hardware registers are sent across the PCI bus and take a significant amount of time to complete after a flush, which causes high priority tasks (including interrupts) to be delayed. This problem stems from an issue with the PCI bus where a fabric lock is held during I/O buffer drain. This process can be detrimental to real-time systems and is seen whenever a large number of MMIO writes are issued. In the case of the e1000 drivers, when a device is reset several tables (MTA, VLAN, etc) are rewritten, generating enough MMIO writes over PCI for the latency issues to be seen. Adding a delay after long series of writes gives them time to complete (drain the I/O buffer), and for higher priority tasks to run unimpeded. Signed-off-by: Jonathan David Signed-off-by: Brad Mouring [bstreiff: remove unused 'rar_count'] Signed-off-by: Brandon Streiff [gratian: update Kconfig '---help---' to 'help'] Signed-off-by: Gratian Crisan --- drivers/net/ethernet/intel/Kconfig | 9 +++++++++ drivers/net/ethernet/intel/e1000/e1000.h | 7 +++++++ drivers/net/ethernet/intel/e1000/e1000_main.c | 5 +++++ drivers/net/ethernet/intel/e1000e/82571.c | 5 +++++ drivers/net/ethernet/intel/e1000e/e1000.h | 7 +++++++ drivers/net/ethernet/intel/e1000e/mac.c | 1 + drivers/net/ethernet/intel/e1000e/netdev.c | 7 +++++++ 7 files changed, 41 insertions(+) diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig index ed8ea63bb1724..67d783b2c810b 100644 --- a/drivers/net/ethernet/intel/Kconfig +++ b/drivers/net/ethernet/intel/Kconfig @@ -84,6 +84,15 @@ config E1000E_HWTS devices. The cross-timestamp is available through the PTP clock driver precise cross-timestamp ioctl (PTP_SYS_OFFSET_PRECISE). +config E1000_DELAY + bool "Add delays to e1000x drivers" + default n + depends on (E1000E || E1000) && PREEMPT_RT_FULL + help + Enable delays after large numbers of MMIO writes to registers. + The delays aid in preventing noticeable impact on real-time + performance when a connection is interrupted. + config IGB tristate "Intel(R) 82575/82576 PCI-Express Gigabit Ethernet support" depends on PCI diff --git a/drivers/net/ethernet/intel/e1000/e1000.h b/drivers/net/ethernet/intel/e1000/e1000.h index 4817eb13ca6f8..d699128f329e3 100644 --- a/drivers/net/ethernet/intel/e1000/e1000.h +++ b/drivers/net/ethernet/intel/e1000/e1000.h @@ -199,6 +199,13 @@ struct e1000_rx_ring { #define E1000_TX_DESC(R, i) E1000_GET_DESC(R, i, e1000_tx_desc) #define E1000_CONTEXT_DESC(R, i) E1000_GET_DESC(R, i, e1000_context_desc) +/* Time to wait after writing large amount of data to registers */ +#ifdef CONFIG_E1000_DELAY +#define E1000_WR_DELAY() usleep_range(50, 100) +#else +#define E1000_WR_DELAY() +#endif + /* board specific private data structure */ struct e1000_adapter { diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c index bed4f040face4..7e2da052230cd 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_main.c +++ b/drivers/net/ethernet/intel/e1000/e1000_main.c @@ -2158,6 +2158,7 @@ static void e1000_enter_82542_rst(struct e1000_adapter *adapter) rctl = er32(RCTL); rctl |= E1000_RCTL_RST; ew32(RCTL, rctl); + E1000_WR_DELAY(); E1000_WRITE_FLUSH(); mdelay(5); @@ -2174,6 +2175,7 @@ static void e1000_leave_82542_rst(struct e1000_adapter *adapter) rctl = er32(RCTL); rctl &= ~E1000_RCTL_RST; ew32(RCTL, rctl); + E1000_WR_DELAY(); E1000_WRITE_FLUSH(); mdelay(5); @@ -2322,6 +2324,9 @@ static void e1000_set_rx_mode(struct net_device *netdev) */ E1000_WRITE_REG_ARRAY(hw, MTA, i, mcarray[i]); } + + E1000_WR_DELAY(); + E1000_WRITE_FLUSH(); if (hw->mac_type == e1000_82542_rev2_0) diff --git a/drivers/net/ethernet/intel/e1000e/82571.c b/drivers/net/ethernet/intel/e1000e/82571.c index 0b1e890dd583b..227a5eb9851f3 100644 --- a/drivers/net/ethernet/intel/e1000e/82571.c +++ b/drivers/net/ethernet/intel/e1000e/82571.c @@ -494,6 +494,8 @@ static void e1000_put_hw_semaphore_82571(struct e1000_hw *hw) { u32 swsm; + E1000_WR_DELAY(); + swsm = er32(SWSM); swsm &= ~(E1000_SWSM_SMBI | E1000_SWSM_SWESMBI); ew32(SWSM, swsm); @@ -545,6 +547,7 @@ static void e1000_put_hw_semaphore_82573(struct e1000_hw *hw) { u32 extcnf_ctrl; + E1000_WR_DELAY(); extcnf_ctrl = er32(EXTCNF_CTRL); extcnf_ctrl &= ~E1000_EXTCNF_CTRL_MDIO_SW_OWNERSHIP; ew32(EXTCNF_CTRL, extcnf_ctrl); @@ -1096,6 +1099,8 @@ static s32 e1000_init_hw_82571(struct e1000_hw *hw) for (i = 0; i < mac->mta_reg_count; i++) E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, 0); + E1000_WR_DELAY(); + /* Setup link and flow control */ ret_val = mac->ops.setup_link(hw); diff --git a/drivers/net/ethernet/intel/e1000e/e1000.h b/drivers/net/ethernet/intel/e1000e/e1000.h index 3178efd980066..a5b87e1ccd55b 100644 --- a/drivers/net/ethernet/intel/e1000e/e1000.h +++ b/drivers/net/ethernet/intel/e1000e/e1000.h @@ -70,6 +70,13 @@ struct e1000_info; /* Time to wait before putting the device into D3 if there's no link (in ms). */ #define LINK_TIMEOUT 100 +/* Time to wait after writing large amount of data to registers */ +#ifdef CONFIG_E1000_DELAY +#define E1000_WR_DELAY() usleep_range(50, 100) +#else +#define E1000_WR_DELAY() +#endif + /* Count for polling __E1000_RESET condition every 10-20msec. * Experimentation has shown the reset can take approximately 210msec. */ diff --git a/drivers/net/ethernet/intel/e1000e/mac.c b/drivers/net/ethernet/intel/e1000e/mac.c index 51512a73fdd07..6aac464ae0cb9 100644 --- a/drivers/net/ethernet/intel/e1000e/mac.c +++ b/drivers/net/ethernet/intel/e1000e/mac.c @@ -335,6 +335,7 @@ void e1000e_update_mc_addr_list_generic(struct e1000_hw *hw, /* replace the entire MTA table */ for (i = hw->mac.mta_reg_count - 1; i >= 0; i--) E1000_WRITE_REG_ARRAY(hw, E1000_MTA, i, hw->mac.mta_shadow[i]); + E1000_WR_DELAY(); e1e_flush(); } diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c index ebcb2a30add09..b827d02909dec 100644 --- a/drivers/net/ethernet/intel/e1000e/netdev.c +++ b/drivers/net/ethernet/intel/e1000e/netdev.c @@ -2943,6 +2943,7 @@ static void e1000_configure_tx(struct e1000_adapter *adapter) e1000e_update_tdt_wa(tx_ring, 0); else writel(0, tx_ring->tail); + E1000_WR_DELAY(); /* Set the Tx Interrupt Delay register */ ew32(TIDV, adapter->tx_int_delay); @@ -3220,6 +3221,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) rctl = er32(RCTL); if (!(adapter->flags2 & FLAG2_NO_DISABLE_RX)) ew32(RCTL, rctl & ~E1000_RCTL_EN); + E1000_WR_DELAY(); e1e_flush(); usleep_range(10000, 11000); @@ -3249,6 +3251,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) ctrl_ext |= E1000_CTRL_EXT_IAME; ew32(IAM, 0xffffffff); ew32(CTRL_EXT, ctrl_ext); + E1000_WR_DELAY(); e1e_flush(); /* Setup the HW Rx Head and Tail Descriptor Pointers and @@ -3268,6 +3271,7 @@ static void e1000_configure_rx(struct e1000_adapter *adapter) e1000e_update_rdt_wa(rx_ring, 0); else writel(0, rx_ring->tail); + E1000_WR_DELAY(); /* Enable Receive Checksum Offload for TCP and UDP */ rxcsum = er32(RXCSUM); @@ -3393,6 +3397,7 @@ static int e1000e_write_uc_addr_list(struct net_device *netdev) ew32(RAH(rar_entries), 0); ew32(RAL(rar_entries), 0); } + E1000_WR_DELAY(); e1e_flush(); return count; @@ -3468,10 +3473,12 @@ static void e1000e_setup_rss_hash(struct e1000_adapter *adapter) netdev_rss_key_fill(rss_key, sizeof(rss_key)); for (i = 0; i < 10; i++) ew32(RSSRK(i), rss_key[i]); + E1000_WR_DELAY(); /* Direct all traffic to queue 0 */ for (i = 0; i < 32; i++) ew32(RETA(i), 0); + E1000_WR_DELAY(); /* Disable raw packet checksumming so that RSS hash is placed in * descriptor on writeback. From f88509886c62918c426d06d48cfd929139cc8388 Mon Sep 17 00:00:00 2001 From: Sundar Subbiah Date: Tue, 2 Apr 2013 15:19:22 -0500 Subject: [PATCH 56/84] wlcore: Depend on WIRELESS_EXT for nitargetcfg This is a hack to get CONFIG_WIRELESS_EXT enabled, since if it is turned on in the defconfig it will not propogate to the .config (as it is not user selectable). Even though the driver does not need it, the current NI user-mode software stacks require this support for scanning and interface enumeration. With this change, it will propogate to the wl12xx driver build (from compat-wireless) and thus include WEXT support in that driver and wireless networking modules. Signed-off-by: Sundar Subbiah Acked-by: James Minor Acked-by: Josh Cartwright Acked-by: Jeff Westfahl Acked-by: Ken Sharp Natinst-ReviewBoard-ID: 37925 --- drivers/net/wireless/ti/wlcore/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/ti/wlcore/Kconfig b/drivers/net/wireless/ti/wlcore/Kconfig index 8094323256fb9..3f652e66e2553 100644 --- a/drivers/net/wireless/ti/wlcore/Kconfig +++ b/drivers/net/wireless/ti/wlcore/Kconfig @@ -3,6 +3,7 @@ config WLCORE tristate "TI wlcore support" depends on MAC80211 select FW_LOADER + select WIRELESS_EXT help This module contains the main code for TI WLAN chips. It abstracts hardware-specific differences among different chipset families. From 034584a4c82b7549aee68241f307c7442fec7494 Mon Sep 17 00:00:00 2001 From: James Minor Date: Mon, 16 Mar 2015 17:22:51 -0500 Subject: [PATCH 57/84] cfg80211: wext: Force scans to occur in AP mode When in AP mode, we should force scans to happen from the wext compatibility layer. This won't hurt when in station mode, and only affects the wl12xx driver. This change is to retain parity with NI's shipped 3.2 kernel. It can be dropped when we switch away from using the WEXT interface, and should not be upstreamed. Signed-off-by: James Minor Reviewed-by: Ben Shelton Reviewed-by: Jaeden Amero Natinst-ReviewBoard-ID: 92027 --- net/wireless/scan.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 11c68b1593245..1f9bc1d191f0b 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -2765,6 +2765,9 @@ int cfg80211_wext_siwscan(struct net_device *dev, /* Set real number of channels specified in creq->channels[] */ creq->n_channels = i; + /* Force the scan if we are in AP mode */ + creq->flags |= NL80211_SCAN_FLAG_AP; + /* translate "Scan for SSID" request */ if (wreq) { if (wrqu->data.flags & IW_SCAN_THIS_ESSID) { From f513a4a43cabf2d30dee2c97c2ee4dbb2cd77a5e Mon Sep 17 00:00:00 2001 From: Nathan Sullivan Date: Thu, 5 Nov 2015 14:53:58 -0600 Subject: [PATCH 58/84] ath6kl: set initial region using DMI info Use the BIOS DMI tables to select an initial region for regulatory info. The region in BIOS will be set in manufacturing based on where the product is to be used. This is intended to facilitate channel and power level selection based on the product's SKU. Signed-off-by: Nathan Sullivan Signed-off-by: James Minor Signed-off-by: Brad Mouring Natinst-CAR-ID: 578408 Natinst-ReviewBoard-ID: 120508 Natinst-ReviewBoard-ID: 130950 Natinst-Trello-ID: https://trello.com/c/xCpOlFJt [bstreiff: remove elvisiii dts changes as part of zynq removal] Signed-off-by: Brandon Streiff [gratian: update Kconfig '---help---' to 'help'] Signed-off-by: Gratian Crisan --- drivers/net/wireless/ath/ath6kl/Kconfig | 9 +++++ drivers/net/wireless/ath/ath6kl/core.c | 50 +++++++++++++++++++++++++ drivers/net/wireless/ath/ath6kl/wmi.c | 6 +++ drivers/net/wireless/ath/ath6kl/wmi.h | 3 ++ 4 files changed, 68 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/Kconfig b/drivers/net/wireless/ath/ath6kl/Kconfig index cd96cf8d99409..a9ac19f2c3727 100644 --- a/drivers/net/wireless/ath/ath6kl/Kconfig +++ b/drivers/net/wireless/ath/ath6kl/Kconfig @@ -64,3 +64,12 @@ config ATH6KL_REGDOMAIN are taken into account. If unsure, say N. + +config ATH6KL_NI_BIOS_DOMAIN + bool "Atheros ath6kl DMI regdomain support" + depends on ATH6KL + help + Enabling this will cause the ath6kl driver to load the initial + regdomain from BIOS DMI tables. + + If unsure, say N. diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index 4f0a7a185fc91..411b92c7229de 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -21,6 +21,8 @@ #include #include #include +#include +#include #include "debug.h" #include "hif-ops.h" @@ -51,6 +53,31 @@ MODULE_PARM_DESC(heart_beat_poll, "Enable fw error detection periodic polling in msecs - Also set recovery_enable for this to be effective"); +#define WLAN_REGION_ID 161 +#ifdef CONFIG_ATH6KL_NI_BIOS_DOMAIN +struct region_table { + struct dmi_header header; + char padding[3]; + char alpha2[2]; +}; + +static char region[2]; +static void find_region_type(const struct dmi_header *dm, void *private_data) +{ + int *found = (int *)private_data; + + if (dm->type == WLAN_REGION_ID) { + struct region_table *table = + container_of(dm, struct region_table, header); + + ath6kl_dbg(ATH6KL_DBG_TRC, "Region code from BIOS: %c%c\n", + table->alpha2[0], table->alpha2[1]); + memcpy(region, table->alpha2, 2); + *found = 1; + } +} +#endif + void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb) { ath6kl_htc_tx_complete(ar, skb); @@ -69,6 +96,20 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) struct wireless_dev *wdev; int ret = 0, i; +#ifdef CONFIG_ATH6KL_NI_BIOS_DOMAIN + /* get region code from DMI */ + dmi_walk(find_region_type, &ret); + if (!ret) + return -ENODEV; + + if (!isascii(region[0]) || !isascii(region[1])) + return -EINVAL; + + ath6kl_info("Using region: %c%c\n", + region[0], + region[1]); +#endif + switch (htc_type) { case ATH6KL_HTC_TYPE_MBOX: ath6kl_htc_mbox_attach(ar); @@ -194,6 +235,15 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) goto err_rxbuf_cleanup; } +#ifdef CONFIG_ATH6KL_NI_BIOS_DOMAIN + /* set region from DMI if it is US */ + if (region[0] == 'U' && region[1] == 'S') { + ret = ath6kl_wmi_set_regdomain_cmd(ar->wmi, region); + if (ret) + goto err_rxbuf_cleanup; + } +#endif + /* give our connected endpoints some buffers */ ath6kl_rx_refill(ar->htc_target, ar->ctrl_ep); ath6kl_rx_refill(ar->htc_target, ar->ac2ep_map[WMM_AC_BE]); diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index bd1ef63349978..691b9876eb0a8 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -3308,9 +3308,15 @@ int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2) cmd = (struct wmi_set_regdomain_cmd *) skb->data; memcpy(cmd->iso_name, alpha2, 2); +#ifdef CONFIG_ATH6KL_SILEX_FIRMWARE + return ath6kl_wmi_cmd_send(wmi, 0, skb, + WMI_SET_REGDOMAIN_SILEX_CMDID, + NO_SYNC_WMIFLAG); +#else return ath6kl_wmi_cmd_send(wmi, 0, skb, WMI_SET_REGDOMAIN_CMDID, NO_SYNC_WMIFLAG); +#endif } s32 ath6kl_wmi_get_rate(struct wmi *wmi, s8 rate_index) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 784940ba4c909..7de770a1f238c 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -644,6 +644,9 @@ enum wmi_cmd_id { WMI_ENABLE_SCHED_SCAN_CMDID, }; +/* Silex has this command at a different ID */ +#define WMI_SET_REGDOMAIN_SILEX_CMDID 0xf0b0 + enum wmi_mgmt_frame_type { WMI_FRAME_BEACON = 0, WMI_FRAME_PROBE_REQ, From 812083a227a5da5169661993082edbd8dcb1faaa Mon Sep 17 00:00:00 2001 From: James Minor Date: Wed, 28 Oct 2015 12:16:56 -0500 Subject: [PATCH 59/84] ath6kl: Add Silex firmware capabilities Signed-off-by: James Minor [gratian: update Kconfig '---help---' to 'help'] Signed-off-by: Gratian Crisan --- drivers/net/wireless/ath/ath6kl/Kconfig | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/Kconfig b/drivers/net/wireless/ath/ath6kl/Kconfig index a9ac19f2c3727..89eddce784310 100644 --- a/drivers/net/wireless/ath/ath6kl/Kconfig +++ b/drivers/net/wireless/ath/ath6kl/Kconfig @@ -73,3 +73,12 @@ config ATH6KL_NI_BIOS_DOMAIN regdomain from BIOS DMI tables. If unsure, say N. + +config ATH6KL_SILEX_FIRMWARE + bool "Atheros ath6kl Silex firmware support" + depends on ATH6KL + help + Enabling this will cause the ath6kl driver to use customizations + required by the firmware from Silex. + + If unsure, say N. From 8b29bc82199ef9d17471bf441ba11490b37efb53 Mon Sep 17 00:00:00 2001 From: Nathan Sullivan Date: Wed, 16 Mar 2016 09:01:15 -0500 Subject: [PATCH 60/84] ath6kl: select board file using DMI info In addition to setting the region on the device, switch board files based on the selected region from BIOS. This allows different boards to load files with different power levels for regulatory purposes. Signed-off-by: Nathan Sullivan Signed-off-by: Brad Mouring Acked-by: Xander Huff Acked-by: James Minor Natinst-CAR-ID: 578408 Natinst-ReviewBoard-ID: 130950 --- drivers/net/wireless/ath/ath6kl/core.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index 411b92c7229de..af953f2329ff2 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -97,6 +97,7 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) int ret = 0, i; #ifdef CONFIG_ATH6KL_NI_BIOS_DOMAIN + char *region_board_file = NULL; /* get region code from DMI */ dmi_walk(find_region_type, &ret); if (!ret) @@ -160,6 +161,17 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) ar->testmode = testmode; +#ifdef CONFIG_ATH6KL_NI_BIOS_DOMAIN + /* ath6kl_init_hw_params() will set the board file name, but we want + * to override it with a region specific board file here. + */ + region_board_file = devm_kzalloc(ar->dev, 64, GFP_KERNEL); + + snprintf(region_board_file, 64, AR6004_HW_3_0_FW_DIR "/bdata%c%c.bin", + region[0], region[1]); + + ar->hw.fw_board = region_board_file; +#endif ret = ath6kl_init_fetch_firmwares(ar); if (ret) goto err_htc_cleanup; From fb7dce51348cb03b051974edf662e9ae89c7b75f Mon Sep 17 00:00:00 2001 From: James Minor Date: Mon, 4 Apr 2016 12:15:18 -0500 Subject: [PATCH 61/84] ath6kl: Add ATH6KL_FW_CAPABILITY_SET_RSN_CAP capabilities Some firmwares offer the ability to directly set the RSN with WMI_SET_RSN_CAP_CMDID instead of using WMI_SET_IE_CMDID. Add the flags and functions to make that work. Signed-off-by: James Minor Signed-off-by: Brad Mouring Natinst-ReviewBoard-ID: 133028 Natinst-CAR-ID: 577496 --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 22 +++++++++++++++------- drivers/net/wireless/ath/ath6kl/core.h | 3 +++ drivers/net/wireless/ath/ath6kl/init.c | 1 + drivers/net/wireless/ath/ath6kl/wmi.c | 18 ++++++++++++++++++ drivers/net/wireless/ath/ath6kl/wmi.h | 6 ++++++ 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index fefdc6753acd2..8ccd8927c5322 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2933,14 +2933,22 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, * advertise the same in beacon/probe response. Send * the complete RSN IE capability field to firmware */ - if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *) &rsn_capab) && - test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, - ar->fw_capabilities)) { - res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx, - WLAN_EID_RSN, WMI_RSN_IE_CAPB, - (const u8 *) &rsn_capab, - sizeof(rsn_capab)); + if (!ath6kl_get_rsn_capab(&info->beacon, (u8 *)&rsn_capab)) { vif->rsn_capab = rsn_capab; + res = -EIO; + if (test_bit(ATH6KL_FW_CAPABILITY_RSN_CAP_OVERRIDE, + ar->fw_capabilities)) { + res = ath6kl_wmi_set_ie_cmd(ar->wmi, vif->fw_vif_idx, + WLAN_EID_RSN, + WMI_RSN_IE_CAPB, + (const u8 *)&rsn_capab, + sizeof(rsn_capab)); + } + if (test_bit(ATH6KL_FW_CAPABILITY_SET_RSN_CAP, + ar->fw_capabilities)) { + res = ath6kl_wmi_set_rsn_cmd(ar->wmi, vif->fw_vif_idx, + rsn_capab); + } if (res < 0) return res; } diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 77e052336eb5b..96c8ab6f096f7 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -151,6 +151,9 @@ enum ath6kl_fw_capability { /* firmware doesn't support IP checksumming */ ATH6KL_FW_CAPABILITY_NO_IP_CHECKSUM, + /* firmware supports setting RSN CAP directly */ + ATH6KL_FW_CAPABILITY_SET_RSN_CAP, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 9b5c7d8f2b95e..8b977e8bf8a97 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1644,6 +1644,7 @@ static const struct fw_capa_str_map { { ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT, "map-lp-endpoint" }, { ATH6KL_FW_CAPABILITY_RATETABLE_MCS15, "ratetable-mcs15" }, { ATH6KL_FW_CAPABILITY_NO_IP_CHECKSUM, "no-ip-checksum" }, + { ATH6KL_FW_CAPABILITY_SET_RSN_CAP, "set-rsn-cap" }, }; static const char *ath6kl_init_get_fw_capa_name(unsigned int id) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 691b9876eb0a8..f9faf4f923c3b 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -3583,6 +3583,24 @@ int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, NO_SYNC_WMIFLAG); } +int ath6kl_wmi_set_rsn_cmd(struct wmi *wmi, u8 if_idx, u16 rsn_capab) +{ + struct sk_buff *skb; + struct wmi_set_rsn_cmd *p; + + skb = ath6kl_wmi_get_new_buf(sizeof(*p)); + if (!skb) + return -ENOMEM; + + ath6kl_info("set_rsn_cmd: rsn_capab=0x%4.4x\n", + rsn_capab); + p = (struct wmi_set_rsn_cmd *)skb->data; + p->rsn_capab = rsn_capab; + + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_RSN_CAP_CMDID, + NO_SYNC_WMIFLAG); +} + int ath6kl_wmi_set_ie_cmd(struct wmi *wmi, u8 if_idx, u8 ie_id, u8 ie_field, const u8 *ie_info, u8 ie_len) { diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 7de770a1f238c..facdd4e3716c0 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -2057,6 +2057,10 @@ struct wmi_set_appie_cmd { u8 ie_info[0]; } __packed; +struct wmi_set_rsn_cmd { + u16 rsn_capab; +} __packed; + struct wmi_set_ie_cmd { u8 ie_id; u8 ie_field; /* enum wmi_ie_field_type */ @@ -2691,6 +2695,8 @@ int ath6kl_wmi_set_rx_frame_format_cmd(struct wmi *wmi, u8 if_idx, u8 rx_meta_version, bool rx_dot11_hdr, bool defrag_on_host); +int ath6kl_wmi_set_rsn_cmd(struct wmi *wmi, u8 if_idx, u16 rsn_capab); + int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, const u8 *ie, u8 ie_len); From 3cc55226f8c562f8bdbc728683d53b64c465e547 Mon Sep 17 00:00:00 2001 From: James Minor Date: Mon, 4 Apr 2016 12:17:46 -0500 Subject: [PATCH 62/84] ath6kl: Add Silex enums to set APPIE and RSN CAP Silex uses different command IDs for the commands WMI_SET_APPIE_CMDID and WMI_SET_RSN_CAP_SILEX_CMDID. In case of the Silex firmware, remap them to the correct command ID. Signed-off-by: James Minor Signed-off-by: Brad Mouring Natinst-ReviewBoard-ID: 133028 Natinst-CAR-ID: 577496 --- drivers/net/wireless/ath/ath6kl/wmi.c | 18 +++++++++++++++--- drivers/net/wireless/ath/ath6kl/wmi.h | 2 ++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index f9faf4f923c3b..36704a4cd843e 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -3579,8 +3579,13 @@ int ath6kl_wmi_set_appie_cmd(struct wmi *wmi, u8 if_idx, u8 mgmt_frm_type, if (ie != NULL && ie_len > 0) memcpy(p->ie_info, ie, ie_len); - return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_APPIE_CMDID, - NO_SYNC_WMIFLAG); +#ifdef CONFIG_ATH6KL_SILEX_FIRMWARE + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_APPIE_SILEX_CMDID, NO_SYNC_WMIFLAG); +#else + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_APPIE_CMDID, NO_SYNC_WMIFLAG); +#endif } int ath6kl_wmi_set_rsn_cmd(struct wmi *wmi, u8 if_idx, u16 rsn_capab) @@ -3597,8 +3602,15 @@ int ath6kl_wmi_set_rsn_cmd(struct wmi *wmi, u8 if_idx, u16 rsn_capab) p = (struct wmi_set_rsn_cmd *)skb->data; p->rsn_capab = rsn_capab; - return ath6kl_wmi_cmd_send(wmi, if_idx, skb, WMI_SET_RSN_CAP_CMDID, +#ifdef CONFIG_ATH6KL_SILEX_FIRMWARE + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_RSN_CAP_SILEX_CMDID, + NO_SYNC_WMIFLAG); +#else + return ath6kl_wmi_cmd_send(wmi, if_idx, skb, + WMI_SET_RSN_CAP_CMDID, NO_SYNC_WMIFLAG); +#endif } int ath6kl_wmi_set_ie_cmd(struct wmi *wmi, u8 if_idx, u8 ie_id, u8 ie_field, diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index facdd4e3716c0..5e44ee92b0b55 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -646,6 +646,8 @@ enum wmi_cmd_id { /* Silex has this command at a different ID */ #define WMI_SET_REGDOMAIN_SILEX_CMDID 0xf0b0 +#define WMI_SET_APPIE_SILEX_CMDID 0x3f +#define WMI_SET_RSN_CAP_SILEX_CMDID 0xf082 enum wmi_mgmt_frame_type { WMI_FRAME_BEACON = 0, From 4578a9ad6fca4c9dbb3e216e5a5801a3eb28273d Mon Sep 17 00:00:00 2001 From: James Minor Date: Thu, 25 Feb 2016 10:18:02 -0600 Subject: [PATCH 63/84] ath6kl: fix firmware race condition by retrying initialization Sometimes the devices will fail to boot the first time, but will work fine when retried. Retry the initialization a few times before failing (and reset the hardware properly when it fails). Signed-off-by: James Minor Signed-off-by: Brad Mouring Natinst-ReviewBoard-ID: 130170 Natinst-CAR-ID: 566029 --- drivers/net/wireless/ath/ath6kl/core.c | 18 +++++++++++++++--- drivers/net/wireless/ath/ath6kl/core.h | 3 +++ drivers/net/wireless/ath/ath6kl/init.c | 2 ++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index af953f2329ff2..90379b73fb04e 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -38,6 +38,7 @@ static unsigned int ath6kl_p2p; static unsigned int testmode; static unsigned int recovery_enable; static unsigned int heart_beat_poll; +static unsigned int boot_attempts; module_param(debug_mask, uint, 0644); module_param(suspend_mode, uint, 0644); @@ -48,9 +49,11 @@ module_param(ath6kl_p2p, uint, 0644); module_param(testmode, uint, 0644); module_param(recovery_enable, uint, 0644); module_param(heart_beat_poll, uint, 0644); +module_param(boot_attempts, uint, 0644); MODULE_PARM_DESC(recovery_enable, "Enable recovery from firmware error"); MODULE_PARM_DESC(heart_beat_poll, "Enable fw error detection periodic polling in msecs - Also set recovery_enable for this to be effective"); +MODULE_PARM_DESC(boot_attempts, "Number of times to retry booting the firmware"); #define WLAN_REGION_ID 161 @@ -111,6 +114,8 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) region[1]); #endif + ar->boot_attempts = boot_attempts; + switch (htc_type) { case ATH6KL_HTC_TYPE_MBOX: ath6kl_htc_mbox_attach(ar); @@ -242,11 +247,18 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) ath6kl_debug_init(ar); ret = ath6kl_init_hw_start(ar); - if (ret) { - ath6kl_err("Failed to start hardware: %d\n", ret); - goto err_rxbuf_cleanup; + while (ret && ar->boot_attempts) { + ath6kl_err("Failed to start hardware: %d (retry %d)\n", + ret, + ar->boot_attempts); + ar->boot_attempts--; + ret = ath6kl_init_hw_start(ar); } + if (ret) + /* Did not boot after several attempts */ + goto err_rxbuf_cleanup; + #ifdef CONFIG_ATH6KL_NI_BIOS_DOMAIN /* set region from DMI if it is US */ if (region[0] == 'U' && region[1] == 'S') { diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 96c8ab6f096f7..2c9c0cac314eb 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -874,6 +874,9 @@ struct ath6kl { u8 disc_timeout; } debug; #endif /* CONFIG_ATH6KL_DEBUG */ + + /* Number of times we have attempted to boot the radio */ + unsigned int boot_attempts; }; static inline struct ath6kl *ath6kl_priv(struct net_device *dev) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 8b977e8bf8a97..db3c750c8c517 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1775,6 +1775,8 @@ static int __ath6kl_init_hw_start(struct ath6kl *ar) WMI_TIMEOUT); if (timeleft <= 0) { clear_bit(WMI_READY, &ar->flag); + ath6kl_init_hw_reset(ar); + ath6kl_bmi_reset(ar); ath6kl_err("wmi is not ready or wait was interrupted: %ld\n", timeleft); ret = -EIO; From 6e58240e3e85dd66cecbf9e77f69cb830746d30d Mon Sep 17 00:00:00 2001 From: James Minor Date: Mon, 25 Apr 2016 12:18:10 -0500 Subject: [PATCH 64/84] ath6kl: Retry SDIO function initialization on failure Sometimes the AR6234 gives us a error "ath6kl: Unable to enable sdio func: -62". This indicates that the enable bit for the function did not result in the function's status bit in the SDIO_CCCR_IORx register getting set within a reasonable period of time (like 400ms). The guess at this point is that the radio is coming up in some strange state and the power-on reset did not clean things up as it should have. This occurs about once every thousand or so reboots. Experimentation has shown that resetting the device after this has happened allows the function enable to correctly set the function's bit in the SDIO_CCCR_IORx. This change will reset the AR6234 and try the function enable again if the module parameter boot_attempts is set to >0. This has been tested over several thousand reboots, and the driver will recover correctly when it would have otherwise failed to initialize. Signed-off-by: James Minor Signed-off-by: Brad Mouring Acked-by: Nathan Sullivan Acked-by: Xander Huff Natinst-ReviewBoard-ID: 135864 Natinst-CAR-ID: 579000 --- drivers/net/wireless/ath/ath6kl/core.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index 90379b73fb04e..a53ab66bc562b 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -142,6 +142,15 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) * seconds. */ ret = ath6kl_hif_power_on(ar); + while (ret && ar->boot_attempts) { + ath6kl_err("Failed to turn on hardware: %d (retry %d)\n", + ret, + ar->boot_attempts); + ar->boot_attempts--; + ret = ath6kl_hif_power_off(ar); + ret = ath6kl_hif_power_on(ar); + } + if (ret) goto err_bmi_cleanup; From c8d7ab20464176f9eaedb14398e918f8f418ae7b Mon Sep 17 00:00:00 2001 From: Wilson Lee Date: Mon, 5 Jun 2017 11:00:51 +0800 Subject: [PATCH 65/84] ni: wifi: ath6kl: Enable BIOS code to load "bdata.XX.bin" board file. Linux upstream had accepted the load of different board file using DTS method with board file name format of "bdata.XX.bin". Hence, we need to modify BIOS code to use same board file name format to match with the upstream code. Signed-off-by: Wilson Lee Signed-off-by: Brad Mouring Acked-by: Keng Soon Cheah Acked-by: Joseph Hershberger Acked-by: Chen Yee Chew Natinst-ReviewBoard-ID: 203623 Perforce-ReviewBoard-ID: 203621 --- drivers/net/wireless/ath/ath6kl/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index a53ab66bc562b..5084562978069 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -181,7 +181,7 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) */ region_board_file = devm_kzalloc(ar->dev, 64, GFP_KERNEL); - snprintf(region_board_file, 64, AR6004_HW_3_0_FW_DIR "/bdata%c%c.bin", + snprintf(region_board_file, 64, AR6004_HW_3_0_FW_DIR "/bdata.%c%c.bin", region[0], region[1]); ar->hw.fw_board = region_board_file; From 8a5246f1f67f02dfe78904f79ca34941af0e4a8a Mon Sep 17 00:00:00 2001 From: James Minor Date: Tue, 27 Feb 2018 16:08:49 -0600 Subject: [PATCH 66/84] net: ath6kl: silex: Separate region and board file selection To support the single antenna board files for products with 1 antenna, separate out the selection of the region from the selection of the board file. On OF platforms, there will now be 2 device tree entries: atheros,region-code - The region code (like US) atheros,board-id - The board file code (like 00, 10, etc) In the process, do a small refactor of the CONFIG_ATH6KL_NI_BIOS_DOMAIN case to be more consistent with the device tree case. Signed-off-by: James Minor Signed-off-by: Brad Mouring Acked-by: Nathan Sullivan Acked-by: Kyle Roeschley Natinst-ReviewBoard-ID: 223288 Natinst-CAR-ID: 686866 --- drivers/net/wireless/ath/ath6kl/core.c | 61 ++--------------- drivers/net/wireless/ath/ath6kl/core.h | 4 ++ drivers/net/wireless/ath/ath6kl/init.c | 90 ++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 57 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index 5084562978069..16873b7b578f3 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -21,8 +21,6 @@ #include #include #include -#include -#include #include "debug.h" #include "hif-ops.h" @@ -56,31 +54,6 @@ MODULE_PARM_DESC(heart_beat_poll, MODULE_PARM_DESC(boot_attempts, "Number of times to retry booting the firmware"); -#define WLAN_REGION_ID 161 -#ifdef CONFIG_ATH6KL_NI_BIOS_DOMAIN -struct region_table { - struct dmi_header header; - char padding[3]; - char alpha2[2]; -}; - -static char region[2]; -static void find_region_type(const struct dmi_header *dm, void *private_data) -{ - int *found = (int *)private_data; - - if (dm->type == WLAN_REGION_ID) { - struct region_table *table = - container_of(dm, struct region_table, header); - - ath6kl_dbg(ATH6KL_DBG_TRC, "Region code from BIOS: %c%c\n", - table->alpha2[0], table->alpha2[1]); - memcpy(region, table->alpha2, 2); - *found = 1; - } -} -#endif - void ath6kl_core_tx_complete(struct ath6kl *ar, struct sk_buff *skb) { ath6kl_htc_tx_complete(ar, skb); @@ -99,21 +72,6 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) struct wireless_dev *wdev; int ret = 0, i; -#ifdef CONFIG_ATH6KL_NI_BIOS_DOMAIN - char *region_board_file = NULL; - /* get region code from DMI */ - dmi_walk(find_region_type, &ret); - if (!ret) - return -ENODEV; - - if (!isascii(region[0]) || !isascii(region[1])) - return -EINVAL; - - ath6kl_info("Using region: %c%c\n", - region[0], - region[1]); -#endif - ar->boot_attempts = boot_attempts; switch (htc_type) { @@ -175,17 +133,6 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) ar->testmode = testmode; -#ifdef CONFIG_ATH6KL_NI_BIOS_DOMAIN - /* ath6kl_init_hw_params() will set the board file name, but we want - * to override it with a region specific board file here. - */ - region_board_file = devm_kzalloc(ar->dev, 64, GFP_KERNEL); - - snprintf(region_board_file, 64, AR6004_HW_3_0_FW_DIR "/bdata.%c%c.bin", - region[0], region[1]); - - ar->hw.fw_board = region_board_file; -#endif ret = ath6kl_init_fetch_firmwares(ar); if (ret) goto err_htc_cleanup; @@ -268,10 +215,10 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) /* Did not boot after several attempts */ goto err_rxbuf_cleanup; -#ifdef CONFIG_ATH6KL_NI_BIOS_DOMAIN - /* set region from DMI if it is US */ - if (region[0] == 'U' && region[1] == 'S') { - ret = ath6kl_wmi_set_regdomain_cmd(ar->wmi, region); +#ifdef CONFIG_ATH6KL_SILEX_FIRMWARE + /* set US region if this device is a US device (US or 1S) */ + if (ar->region[0] == 'U' && ar->region[1] == 'S') { + ret = ath6kl_wmi_set_regdomain_cmd(ar->wmi, ar->region); if (ret) goto err_rxbuf_cleanup; } diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 2c9c0cac314eb..6fdbaf977154d 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -877,6 +877,10 @@ struct ath6kl { /* Number of times we have attempted to boot the radio */ unsigned int boot_attempts; + +#ifdef CONFIG_ATH6KL_SILEX_FIRMWARE + unsigned char region[2]; +#endif }; static inline struct ath6kl *ath6kl_priv(struct net_device *dev) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index db3c750c8c517..81e11498706f3 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -24,6 +24,8 @@ #include #include #include +#include +#include #include "core.h" #include "cfg80211.h" @@ -32,6 +34,8 @@ #include "hif-ops.h" #include "htc-ops.h" +#define WLAN_REGION_ID 161 + static const struct ath6kl_hw hw_list[] = { { .id = AR6003_HW_2_0_VERSION, @@ -706,6 +710,10 @@ static bool check_device_tree(struct ath6kl *ar) char board_filename[64]; const char *board_id; int ret; +#ifdef CONFIG_ATH6KL_SILEX_FIRMWARE + static const char *region_code_prop = "atheros,region-code"; + const char *region_code; +#endif for_each_compatible_node(node, NULL, "atheros,ath6kl") { board_id = of_get_property(node, board_id_prop, NULL); @@ -724,6 +732,16 @@ static bool check_device_tree(struct ath6kl *ar) board_filename, ret); continue; } +#ifdef CONFIG_ATH6KL_SILEX_FIRMWARE + region_code = of_get_property(node, region_code_prop, NULL); + if (region_code == NULL) { + ath6kl_warn("No \"%s\" property on %s node.\n", + region_code_prop, node->name); + continue; + } + /* Silex firmware needs a region command in some cases */ + memcpy(&ar->region, region_code, 2); +#endif of_node_put(node); return true; } @@ -736,6 +754,73 @@ static bool check_device_tree(struct ath6kl *ar) } #endif /* CONFIG_OF */ +#ifdef CONFIG_ATH6KL_NI_BIOS_DOMAIN +struct region_table { + struct dmi_header header; + char padding[3]; + char alpha2[2]; +}; + +static char region[2]; +static void find_region_type(const struct dmi_header *dm, void *private_data) +{ + int *found = (int *)private_data; + + if (dm->type == WLAN_REGION_ID) { + struct region_table *table = + container_of(dm, struct region_table, header); + + memcpy(region, table->alpha2, 2); + *found = 1; + } +} + +static bool check_ni_bios(struct ath6kl *ar) +{ + char *region_board_file = NULL; + int ret = 0; + + /* get region code from DMI */ + dmi_walk(find_region_type, &ret); + if (!ret) + return -ENODEV; + + if (!isascii(region[0]) || !isascii(region[1])) + return -EINVAL; + + ath6kl_info("Using BIOS region: %c%c\n", + region[0], + region[1]); + +#ifdef CONFIG_ATH6KL_SILEX_FIRMWARE + /* Silex firmware needs a region command in some cases */ + memcpy(&ar->region, region, 2); +#endif + + /* ath6kl_init_hw_params() will set the board file name, but we want + * to override it with a region specific board file here. + */ + region_board_file = devm_kzalloc(ar->dev, 64, GFP_KERNEL); + + snprintf(region_board_file, 64, AR6004_HW_3_0_FW_DIR "/bdata.%c%c.bin", + region[0], region[1]); + + ret = ath6kl_get_fw(ar, region_board_file, &ar->fw_board, + &ar->fw_board_len); + if (ret) { + ath6kl_err("Failed to get BIOS board file %s: %d\n", + region_board_file, ret); + return false; + } + return true; +} +#else +static bool check_ni_bios(struct ath6kl *ar) +{ + return false; +} +#endif /* CONFIG_ATH6KL_NI_BIOS_DOMAIN */ + static int ath6kl_fetch_board_file(struct ath6kl *ar) { const char *filename; @@ -761,6 +846,11 @@ static int ath6kl_fetch_board_file(struct ath6kl *ar) return 0; } + if (check_ni_bios(ar)) { + /* got board file from BIOS */ + return 0; + } + /* there was no proper board file, try to use default instead */ ath6kl_warn("Failed to get board file %s (%d), trying to find default board file.\n", filename, ret); From bc77804a6b6f0784afcb2188425659570969f329 Mon Sep 17 00:00:00 2001 From: Chen Yee Chew Date: Thu, 28 May 2015 21:11:02 +0800 Subject: [PATCH 67/84] HACK sd: Prevent SD from doing high-speed timing This will prevent SD card from doing SD high-speed timing. It will not do SD high-speed timing for high-speed or standard-speed card. Signed-off-by: Chen Yee Chew Reviewed-by: Keng Soon Cheah Reviewed-by: Joe Hershberger Natinst-CAR-ID: 519438 Natinst-ReviewBoard-ID: 99036 [bstreiff: convert to device_property_present as per 8199d312dad7 ("mmc: sdhci-pltfm: Convert DT properties to generic device properties")] Signed-off-by: Brandon Streiff --- drivers/mmc/host/sdhci-pltfm.c | 3 +++ drivers/mmc/host/sdhci.c | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci-pltfm.c b/drivers/mmc/host/sdhci-pltfm.c index 328b132bbe572..f0948c167c16e 100644 --- a/drivers/mmc/host/sdhci-pltfm.c +++ b/drivers/mmc/host/sdhci-pltfm.c @@ -97,6 +97,9 @@ void sdhci_get_property(struct platform_device *pdev) if (device_property_present(dev, "broken-cd")) host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION; + if (device_property_present(dev, "force-sd-standard")) + host->quirks |= SDHCI_QUIRK_NO_HISPD_BIT; + if (device_property_present(dev, "no-1-8-v")) host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V; diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 8eefa7d5fe85e..68dd1d2c31cb0 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -4391,7 +4391,8 @@ int sdhci_setup_host(struct sdhci_host *host) if (host->quirks2 & SDHCI_QUIRK2_HOST_NO_CMD23) mmc->caps &= ~MMC_CAP_CMD23; - if (host->caps & SDHCI_CAN_DO_HISPD) + if ((host->caps & SDHCI_CAN_DO_HISPD) && + !(host->quirks & SDHCI_QUIRK_NO_HISPD_BIT)) mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; if ((host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) && From bccc45e442501872b120b668365b1eba74d68c89 Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Thu, 31 Aug 2017 19:58:11 -0500 Subject: [PATCH 68/84] mmc: core: Wait for Vdd to settle on card power off The SD spec version 6.0 section 6.4.1.5 requires that Vdd must be lowered to less than 0.5V for a minimum of 1 ms when powering off a card. Increase our wait to 15 ms so that voltage has time to drain down to 0.5V. Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: James Minor Acked-by: Brandon Streiff Natinst-ReviewBoard-ID: 203804 --- drivers/mmc/host/sdhci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 68dd1d2c31cb0..a8e4a91180aae 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2290,6 +2290,9 @@ void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) else sdhci_set_power(host, ios->power_mode, ios->vdd); + if (ios->power_mode == MMC_POWER_OFF) + mdelay(15); + if (host->ops->platform_send_init_74_clocks) host->ops->platform_send_init_74_clocks(host, ios->power_mode); From 20a48ae86afae8edb9b3143551d28f053bc700e5 Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Tue, 24 Apr 2018 08:27:03 -0500 Subject: [PATCH 69/84] mmc: sdhci: Disable SD card clock before changing parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the SD Host Controller Simplified Specification v4.20 §3.2.3, change the SD card clock parameters only after first disabling the external card clock. Doing this fixes a spurious clock pulse on Baytrail and Apollo Lake SD controllers which otherwise breaks voltage switching with a specific Swissbit SD card. Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: Tony Liechty Acked-by: Nathan Sullivan Natinst-ReviewBoard-ID: 236135 Natinst-CAR-ID: 694815 --- drivers/mmc/host/sdhci.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index a8e4a91180aae..2dead509ec50c 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -1996,10 +1996,15 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) host->mmc->actual_clock = 0; - sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); + clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL); + if (clk & SDHCI_CLOCK_CARD_EN) + sdhci_writew(host, clk & ~SDHCI_CLOCK_CARD_EN, + SDHCI_CLOCK_CONTROL); - if (clock == 0) + if (clock == 0) { + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); return; + } clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); sdhci_enable_clk(host, clk); From a32786916175e80e7868a85b414f5f17b95709ca Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Mon, 14 May 2018 13:58:17 -0500 Subject: [PATCH 70/84] mmc: sdhci: Add quirk for delay between clock disable and param change Some SD controllers require a delay between clearing SDHCI_CLOCK_CARD_EN and changing the SD clock dividers in order to avoid a runt clock pulse which can otherwise cause problems with some SD cards. Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: Tony Liechty Acked-by: Nathan Sullivan Natinst-ReviewBoard-ID: 236135 [bstreiff: minor fix to renumber flag bit] Signed-off-by: Brandon Streiff --- drivers/mmc/host/sdhci.c | 6 ++++++ drivers/mmc/host/sdhci.h | 2 ++ 2 files changed, 8 insertions(+) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2dead509ec50c..0e1b689c90b73 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2001,6 +2001,12 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_writew(host, clk & ~SDHCI_CLOCK_CARD_EN, SDHCI_CLOCK_CONTROL); + if (host->quirks2 & SDHCI_QUIRK2_NEED_DELAY_AFTER_CLK_DISABLE) { + spin_unlock_irq(&host->lock); + usleep_range(900, 1100); + spin_lock_irq(&host->lock); + } + if (clock == 0) { sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); return; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index e8d04e42a5afd..fa7a98b2a8fbb 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -476,6 +476,8 @@ struct sdhci_host { * block count. */ #define SDHCI_QUIRK2_USE_32BIT_BLK_CNT (1<<18) +/* Controller requires delay between card clock disable and param change */ +#define SDHCI_QUIRK2_NEED_DELAY_AFTER_CLK_DISABLE (1<<19) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ From aefd6097706485569da16eafaff48cf397f88bc2 Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Wed, 16 May 2018 16:45:01 -0500 Subject: [PATCH 71/84] mmc: sdhci: Add quirk work around double rescan On some SD host controllers, we can see two SD card insert interrupts when inserting a card. This causes mmc_rescan() to be called twice in quick succession and generate one or two SD card clock pulses, which can cause some SD cards to become unresponsive. Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: Tony Liechty Acked-by: Nathan Sullivan Natinst-ReviewBoard-ID: 236135 [gratian: fix conflict with fec796739740 ("mmc: sdhci: Factor out sdhci_enable_clk")] Signed-off-by: Gratian Crisan [bstreiff: minor change to renumber flag bit] Signed-off-by: Brandon Streiff --- drivers/mmc/host/sdhci.c | 8 +++++++- drivers/mmc/host/sdhci.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 0e1b689c90b73..9fa813615e6c2 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2009,11 +2009,17 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) if (clock == 0) { sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); - return; + goto out_delay; } clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); sdhci_enable_clk(host, clk); +out_delay: + if (host->quirks2 & SDHCI_QUIRK2_SPURIOUS_CARD_INSERT_INTERRUPT) { + spin_unlock_irq(&host->lock); + usleep_range(4900, 5100); + spin_lock_irq(&host->lock); + } } EXPORT_SYMBOL_GPL(sdhci_set_clock); diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index fa7a98b2a8fbb..d5e917ac3e2cd 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -478,6 +478,8 @@ struct sdhci_host { #define SDHCI_QUIRK2_USE_32BIT_BLK_CNT (1<<18) /* Controller requires delay between card clock disable and param change */ #define SDHCI_QUIRK2_NEED_DELAY_AFTER_CLK_DISABLE (1<<19) +/* Controller may interrupt multiple times for card insert */ +#define SDHCI_QUIRK2_SPURIOUS_CARD_INSERT_INTERRUPT (1<<20) int irq; /* Device IRQ */ void __iomem *ioaddr; /* Mapped address */ From f8b9e261c9833e6c16a3d72f673ee1e08498640a Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Thu, 17 May 2018 11:25:47 -0500 Subject: [PATCH 72/84] mmc: sdhci-pci: Use two delay quirks for Baytrail SD With the Baytrail SD controllers on cRIO-905x, we can run into two conditions which cause functional problems with the NI-recommended microSD card. The first is a runt pulse after SD card clock disable, which is fixed by using SDHCI_QUIRK2_NEED_DELAY_AFTER_CLK_DISABLE to wait after disabling the clock. The second is receiving two SDHCI_INT_CARD_INSERT interrupts, which causes us to set up the card twice a make our recommended microSD card unresponsive. Work around this by using SDHCI_QUIRK2_SPURIOUS_CARD_INSERT_INTERRUPT. Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: Tony Liechty Acked-by: Nathan Sullivan Natinst-ReviewBoard-ID: 236135 Natinst-CAR-ID: 696865 Natinst-CAR-ID: 694815 --- drivers/mmc/host/sdhci-pci-core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index be19785227fe4..c1502736fd627 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -1177,6 +1177,10 @@ static int byt_sd_probe_slot(struct sdhci_pci_slot *slot) slot->chip->pdev->subsystem_device == PCI_SUBDEVICE_ID_NI_78E3) slot->host->mmc->caps2 |= MMC_CAP2_AVOID_3_3V; + if (slot->chip->pdev->device == PCI_DEVICE_ID_INTEL_BYT_SD) + slot->host->quirks2 |= SDHCI_QUIRK2_NEED_DELAY_AFTER_CLK_DISABLE | + SDHCI_QUIRK2_SPURIOUS_CARD_INSERT_INTERRUPT; + byt_needs_pwr_off(slot); return 0; From 41b563bd38246ec1e4be47bb2f28697c328e46b6 Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Tue, 15 May 2018 13:56:50 -0500 Subject: [PATCH 73/84] mmc: sdhci: Handle tuning error interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per the §2.2.19 of the SD Host Controller Simplified Specification Version 4.20, the Tuning Error interrupt is set when an unrecoverable error is detected by the host controller in the tuning circuit when not executing the tuning procedure. Handle this interrupt by printing a useful error message and re-tuning (also per the spec). Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: Tony Liechty Acked-by: Nathan Sullivan Natinst-ReviewBoard-ID: 236135 Natinst-CAR-ID: 696866 [gratian: adjust includes for 5857b29b96dc ("mmc: core: Move public functions from host.h to private headers")] Signed-off-by: Gratian Crisan --- drivers/mmc/host/sdhci.c | 24 ++++++++++++++++++++++-- drivers/mmc/host/sdhci.h | 1 + 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 9fa813615e6c2..2a80c221a9b29 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -32,6 +32,7 @@ #include #include +#include "../core/host.h" #include "sdhci.h" #define DRIVER_NAME "sdhci" @@ -264,7 +265,7 @@ static void sdhci_set_default_irqs(struct sdhci_host *host) if (host->tuning_mode == SDHCI_TUNING_MODE_2 || host->tuning_mode == SDHCI_TUNING_MODE_3) - host->ier |= SDHCI_INT_RETUNE; + host->ier |= SDHCI_INT_RETUNE | SDHCI_INT_TUNING_ERR; sdhci_writel(host, host->ier, SDHCI_INT_ENABLE); sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE); @@ -3514,6 +3515,24 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) if (intmask & SDHCI_INT_RETUNE) mmc_retune_needed(host->mmc); + if (intmask & SDHCI_INT_TUNING_ERR) { + u16 ctrl2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); + /* + * Only complain and retune if we're actually using the + * host's tuning circuits. + */ + if (ctrl2 & SDHCI_CTRL_TUNED_CLK) { + sdhci_writew(host, + ctrl2 & ~SDHCI_CTRL_TUNED_CLK, + SDHCI_HOST_CONTROL2); + mmc_retune_recheck(host->mmc); + pr_err("%s: Unrecoverable error in tuning circuit\n", + mmc_hostname(host->mmc)); + } + sdhci_writel(host, SDHCI_INT_TUNING_ERR, + SDHCI_INT_STATUS); + } + if ((intmask & SDHCI_INT_CARD_INT) && (host->ier & SDHCI_INT_CARD_INT)) { sdhci_enable_sdio_irq_nolock(host, false); @@ -3523,7 +3542,8 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE | SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK | SDHCI_INT_ERROR | SDHCI_INT_BUS_POWER | - SDHCI_INT_RETUNE | SDHCI_INT_CARD_INT); + SDHCI_INT_RETUNE | SDHCI_INT_TUNING_ERR | + SDHCI_INT_CARD_INT); if (intmask) { unexpected |= intmask; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index d5e917ac3e2cd..3ba031c5c8541 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -151,6 +151,7 @@ #define SDHCI_INT_BUS_POWER 0x00800000 #define SDHCI_INT_AUTO_CMD_ERR 0x01000000 #define SDHCI_INT_ADMA_ERROR 0x02000000 +#define SDHCI_INT_TUNING_ERR 0x04000000 #define SDHCI_INT_NORMAL_MASK 0x00007FFF #define SDHCI_INT_ERROR_MASK 0xFFFF8000 From 30c09fa3515df7c458da538dea3294169f35e6e3 Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Tue, 20 Nov 2018 07:38:02 -0600 Subject: [PATCH 74/84] mmc: sdhci: Do not disable interrupts in sdhci_set_clock Upstream removed spin lock usage in the set_ios path with commit d1e4f74f911d ("mmc: sdhci: Do not use spin lock in set_ios paths"), which means that our calls to spin_(un)lock_irq() in sdhci_set_clock() now cause an error on boot if a card is not present or a crash if one is present. Remove these calls so we don't do that and to match upstream's change. Fixes: 815777e0a1fb2 ("mmc: sdhci: Add quirk for delay between clock disable and param change") Fixes: 30d2f458ba48f ("mmc: sdhci: Add quirk work around double rescan") Signed-off-by: Kyle Roeschley Acked-by: Gratian Crisan Acked-by: Brandon Streiff Natinst-ReviewBoard-ID: 264712 Natinst-CAR-ID: 720310 --- drivers/mmc/host/sdhci.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 2a80c221a9b29..94ee068072e55 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -2002,11 +2002,8 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) sdhci_writew(host, clk & ~SDHCI_CLOCK_CARD_EN, SDHCI_CLOCK_CONTROL); - if (host->quirks2 & SDHCI_QUIRK2_NEED_DELAY_AFTER_CLK_DISABLE) { - spin_unlock_irq(&host->lock); - usleep_range(900, 1100); - spin_lock_irq(&host->lock); - } + if (host->quirks2 & SDHCI_QUIRK2_NEED_DELAY_AFTER_CLK_DISABLE) + mdelay(1); if (clock == 0) { sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); @@ -2016,11 +2013,8 @@ void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); sdhci_enable_clk(host, clk); out_delay: - if (host->quirks2 & SDHCI_QUIRK2_SPURIOUS_CARD_INSERT_INTERRUPT) { - spin_unlock_irq(&host->lock); - usleep_range(4900, 5100); - spin_lock_irq(&host->lock); - } + if (host->quirks2 & SDHCI_QUIRK2_SPURIOUS_CARD_INSERT_INTERRUPT) + mdelay(5); } EXPORT_SYMBOL_GPL(sdhci_set_clock); From 223c06aa9511810bf7ae3527c79a7cf22c222bac Mon Sep 17 00:00:00 2001 From: Haris Okanovic Date: Fri, 11 Jan 2019 15:06:18 -0600 Subject: [PATCH 75/84] drivers/firmware/efi/Kconfig: Add EFI_RUNTIME Add Kconfig option to enable EFI runtime services by default on !PREEMPT_RT_BASE kernels, unless otherwise configured. This change refactors Sebastian Siewior's commit 55544e1d (efi: Disable runtime services on RT) as a Kconfig token, so that default behavior may be toggled at build time. There's no behavioral change. Signed-off-by: Haris Okanovic Acked-by: Julia Cartwright Acked-by: Gratian Crisan [bstreiff: change from PREEMPT_RT_BASE to PREEMPT_RT as per upstream commit change 987d6084b96c ("efi: Disable runtime services on RT")] Signed-off-by: Brandon Streiff --- drivers/firmware/efi/Kconfig | 9 +++++++++ drivers/firmware/efi/efi.c | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 2c3dac5ecb36d..a9e5d489d1065 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -14,6 +14,15 @@ config EFI_VARS legacy users: new users should use the efivarfs filesystem instead. +config EFI_RUNTIME + bool "Enable EFI runtime services on boot by default" + depends on EFI + default y if !PREEMPT_RT + help + Set Y to enable EFI runtime services on boot by default. + This selection is overridden by "efi=runtime" and "efi=noruntime" + boot parameters. + config EFI_ESRT bool depends on EFI && !IA64 diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index ae79c33001297..9249479a461a8 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -66,7 +66,7 @@ struct mm_struct efi_mm = { struct workqueue_struct *efi_rts_wq; -static bool disable_runtime = IS_ENABLED(CONFIG_PREEMPT_RT); +static bool disable_runtime = !IS_ENABLED(CONFIG_EFI_RUNTIME); static int __init setup_noefi(char *arg) { disable_runtime = true; From 635bef817c5963dcc9b32cc63f5188bbb6e7bb7b Mon Sep 17 00:00:00 2001 From: Wan Ahmad Zainie Date: Tue, 14 Mar 2017 15:45:21 -0500 Subject: [PATCH 76/84] usb: dwc3: call _DSM for core soft reset The issue is, if core soft reset is issued while Intel Apollo Lake USB mux is in Host role mode, it takes close to 7 minutes before we are able to switch USB mux from Host mode to Device mode. This is due to RTL bug. The workaround is to let BIOS issue the core soft reset via _DSM method. It will ensure that USB mux is in Device role mode before issuing core soft reset, and will inform the driver whether the reset is success within the timeout value, or the timeout is exceeded. commit cd78b8067c6e ("usb: dwc3: call _DSM for core soft reset") originated from http://git.yoctoproject.org/cgit/cgit.cgi/linux-yocto-4.1/ Signed-off-by: Wan Ahmad Zainie [akash.mankar@ni.com: changed the way has_dsm_for_softreset property is set in dwc3-pci.c and read in core.c] Signed-off-by: Akash Mankar Signed-off-by: Brad Mouring Acked-by: Gratian Crisan Acked-by: Brandon Streiff Natinst-ReviewBoard-ID: 178124 [gratian: fixed merge conflicts, mainly due to dwc3_soft_reset removal] Signed-off-by: Gratian Crisan [bstreiff: fixed merge conflicts due to property refactor by 1a7b12f69a94 ("usb: dwc3: pci: Supply device properties via driver data")] [gratian: fix merge conflict with f580170f135a ("usb: dwc3: Add splitdisable quirk for Hisilicon Kirin Soc")] Signed-off-by: Gratian Crisan --- drivers/usb/dwc3/core.c | 40 +++++++++++++++++++++++++++++++++++++ drivers/usb/dwc3/core.h | 3 +++ drivers/usb/dwc3/dwc3-pci.c | 1 + 3 files changed, 44 insertions(+) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 0104a80b185e1..d335ce7e03014 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -256,6 +256,38 @@ u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type) return DWC3_GDBGFIFOSPACE_SPACE_AVAILABLE(reg); } +/** + * WORKAROUND: We let BIOS issues the core soft reset to Device + * controller for Intel Apollo Lake, via _DSM method. + * + * The issue is, if core soft reset is issued while Intel Apollo Lake + * USB mux is in Host role mode, it takes close to 7 minutes before + * we are able to switch USB mux from Host mode to Device mode. + */ +static int dwc3_pci_dsm_soft_reset(struct device *dev) +{ + int ret = -ETIMEDOUT; + union acpi_object *obj; + guid_t guid; + + WARN_ON(guid_parse("732b85d5-b7a7-4a1b-9ba0-4bbd00ffd511", &guid)); + + obj = acpi_evaluate_dsm(ACPI_HANDLE(dev), + &guid, + 1, 6, NULL); + if (!obj) { + dev_err(dev, "failed to evaluate _DSM\n"); + return -EIO; + } + + if (obj->type == ACPI_TYPE_INTEGER) + ret = (obj->integer.value == 0) ? 0 : -ETIMEDOUT; + dev_dbg(dev, "dwc3_pci_dsm_soft_reset() ret= %d\n", ret); + + ACPI_FREE(obj); + return ret; +} + /** * dwc3_core_soft_reset - Issues core soft reset and PHY reset * @dwc: pointer to our context structure @@ -273,6 +305,11 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_HOST) return 0; + if (dwc->has_dsm_for_softreset) { + dev_dbg(dwc->dev, "calling dwc3_pci_dsm_soft_reset()"); + return dwc3_pci_dsm_soft_reset(dwc->dev); + } + reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg |= DWC3_DCTL_CSFTRST; dwc3_writel(dwc->regs, DWC3_DCTL, reg); @@ -1396,6 +1433,9 @@ static void dwc3_get_properties(struct dwc3 *dwc) dwc->dis_split_quirk = device_property_read_bool(dev, "snps,dis-split-quirk"); + dwc->has_dsm_for_softreset = device_property_read_bool(dev, + "snps,has_dsm_for_softreset"); + dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 5612bfdf37da9..d29a1dfd57a2a 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1080,6 +1080,7 @@ struct dwc3_scratchpad_array { * 3 - Reserved * @dis_metastability_quirk: set to disable metastability quirk. * @dis_split_quirk: set to disable split boundary. + * @has_dsm_for_softreset: set if we want to use BIOS to do core soft reset * @imod_interval: set the interrupt moderation interval in 250ns * increments or 0 to disable. * @max_cfg_eps: current max number of IN eps used across all USB configs. @@ -1291,6 +1292,8 @@ struct dwc3 { unsigned dis_split_quirk:1; unsigned async_callbacks:1; + unsigned has_dsm_for_softreset:1; + u16 imod_interval; int max_cfg_eps; diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 7ff8fc8f79a9b..62e258a0fed8f 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -115,6 +115,7 @@ static int dwc3_byt_enable_ulpi_refclock(struct pci_dev *pci) static const struct property_entry dwc3_pci_intel_properties[] = { PROPERTY_ENTRY_STRING("dr_mode", "peripheral"), + PROPERTY_ENTRY_BOOL("snps,has_dsm_for_softreset"), PROPERTY_ENTRY_BOOL("linux,sysdev_is_parent"), {} }; From dcc27d866bdf68482cc62b0c3c2d73a699700425 Mon Sep 17 00:00:00 2001 From: Richard Tollerton Date: Wed, 4 Apr 2012 20:13:05 -0400 Subject: [PATCH 77/84] ftrace: ni: add raw marker support for trace tool Signed-off-by: Richard Tollerton [gratian: fix conflict with fa32e8557b47 ("tracing: Add new trace_marker_raw"); rename NI specific implementation until we can replace it] Signed-off-by: Gratian Crisan [bstreiff: fixups due to struct member renames in 13292494379f ("tracing: Make struct ring_buffer less ambiguous")] Signed-off-by: Brandon Streiff [gratian: update for 22c36b182634 ("tracing: make tracing_init_dentry() returns an integer instead of a d_entry pointer")] Signed-off-by: Gratian Crisan [gratian: fixups due to tracing contex introduction in edbaaa13a660 ("tracing: Merge irqflags + preemt counter, add RT bits")] Signed-off-by: Gratian Crisan [gratian: fixups due to tracing_gen_ctx_flags() API change in 8cac5dbf18c7 ("tracing: Merge irqflags + preemt counter, add RT bits")] Signed-off-by: Gratian Crisan [gratian: use always_inlined __trace_buffer_lock_reserve() introduced by 3e9a8aadca48 ("tracing: Create a always_inlined __trace_buffer_lock_reserve()")] Signed-off-by: Gratian Crisan --- kernel/trace/trace.c | 92 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 3471d5c66e46e..7eee99a8920e1 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -7189,6 +7189,89 @@ tracing_mark_raw_write(struct file *filp, const char __user *ubuf, return written; } +/* + * rtollert: tracing_ni_ett_raw_write exists as part of LabVIEW RT's support of + * the Execution Trace Toolkit. LabVIEW RT logs its own events through this + * interface, so that they are stored in ftrace's ring buffers. Basically + * tracing_ni_ett_raw_write is the same as tracing_mark_write, except all the + * text processing code is ripped out for improved performance. + * + * These events will show up as BPRINT ftrace events, with ip and fmt set to + * the fourcc 'lvrt'. The event data is generally a binary blob that is + * processed later by LabVIEW RT (and ultimately the ETT). That data is not + * meant to be parsed by third parties and is not documented (sorry). + * + * I'm a little embarrassed of this implementation, so this code goes out of + * its way to scream "HACK!": The hardcoded settings for ip and fmt; the + * name of the marker file (trace_ni_ett_marker), etc. + * + * Eventually I'd like to see a solution which would allow multiple programs + * to each write to their own marker files, with dynamically allocated IDs, + * without overloading BPRINT events, etc. However a lot of that is contingent + * on if it's even a good idea to allow binary blobs to be logged to ftrace. + * (a worthwhile discussion!) + */ +static ssize_t +tracing_ni_ett_raw_write(struct file *filp, const char __user *ubuf, + size_t cnt, loff_t *fpos) +{ + struct trace_event_call *call = &event_bprint; + struct ring_buffer_event *event; + struct trace_array *tr = &global_trace; + struct trace_buffer *buffer = tr->array_buffer.buffer; + struct trace_array_cpu *data; + int cpu, size; + unsigned int trace_ctx; + struct bprint_entry *entry; + unsigned long irq_flags; + int disable; + + const unsigned int ip = 0x6c767274; /* "lvrt" */ + const char *fmt = "lvrt"; /* to avoid dereferencing NULL */ + + if (tracing_disabled || tracing_selftest_running) + return -EINVAL; + + preempt_disable_notrace(); + cpu = raw_smp_processor_id(); + data = per_cpu_ptr(tr->array_buffer.data, cpu); + disable = atomic_inc_return(&data->disabled); + if (unlikely(disable != 1)) + goto out; + pause_graph_tracing(); + raw_local_irq_save(irq_flags); + + trace_ctx = tracing_gen_ctx_flags(irq_flags); + size = sizeof(*entry) + cnt; + event = __trace_buffer_lock_reserve(buffer, TRACE_BPRINT, size, + trace_ctx); + if (!event) + goto out_unlock; + entry = ring_buffer_event_data(event); + entry->ip = ip; + entry->fmt = fmt; + + if (cnt) { + if (copy_from_user(&(entry->buf[0]), ubuf, cnt)) { + cnt = -EFAULT; + goto error_and_trace; + } + } + if (call_filter_check_discard(call, entry, buffer, event)) + goto out_unlock; + error_and_trace: + __buffer_unlock_commit(buffer, event); + ftrace_trace_stack(&global_trace, buffer, trace_ctx, 6, NULL); + out_unlock: + raw_local_irq_restore(irq_flags); + unpause_graph_tracing(); + out: + atomic_dec_return(&data->disabled); + preempt_enable_notrace(); + + return cnt; +} + static int tracing_clock_show(struct seq_file *m, void *v) { struct trace_array *tr = m->private; @@ -7598,6 +7681,12 @@ static const struct file_operations tracing_mark_raw_fops = { .release = tracing_release_generic_tr, }; +static const struct file_operations tracing_ni_ett_raw_fops = { + .open = tracing_open_generic, + .write = tracing_ni_ett_raw_write, + .llseek = generic_file_llseek, +}; + static const struct file_operations trace_clock_fops = { .open = tracing_clock_open, .read = seq_read, @@ -9691,6 +9780,9 @@ static __init int tracer_init_tracefs(void) trace_create_file("README", 0444, NULL, NULL, &tracing_readme_fops); + trace_create_file("trace_ni_ett_marker", 0220, NULL, + NULL, &tracing_ni_ett_raw_fops); + trace_create_file("saved_cmdlines", 0444, NULL, NULL, &tracing_saved_cmdlines_fops); From 62c5f0aea449ab2ac551b5368fc75f111e8776ba Mon Sep 17 00:00:00 2001 From: Gratian Crisan Date: Thu, 24 Mar 2016 13:52:00 -0500 Subject: [PATCH 78/84] time: Make the clocksource watchdog user configurable The clocksource watchdog is used to detect instabilities in the current clocksource. This is a beneficial feature on new/unknown hardware however it can create problems by falsely triggering when the watchdog wraps. The reason is that an interrupt storm and/or high priority (FIFO/RR) tasks can preempt the timer softirq long enough for the watchdog to wrap if it has a limited number of bits available by comparison with the main clocksource. One observed example is on a Intel Baytrail platform where TSC is the main clocksource, HPET is disabled due to a hardware bug and acpi_pm gets selected as the watchdog clocksource. Provide the option to disable the clocksource watchdog for hardware where the clocksource stability has been validated. Signed-off-by: Gratian Crisan --- arch/x86/Kconfig | 2 +- kernel/time/Kconfig | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 0260daadd5d8e..26b7b0aabbbbe 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -124,7 +124,7 @@ config X86 select BUILDTIME_TABLE_SORT select CLKEVT_I8253 select CLOCKSOURCE_VALIDATE_LAST_CYCLE - select CLOCKSOURCE_WATCHDOG + select HAVE_CLOCKSOURCE_WATCHDOG select DCACHE_WORD_ACCESS select EDAC_ATOMIC_SCRUB select EDAC_SUPPORT diff --git a/kernel/time/Kconfig b/kernel/time/Kconfig index 04bfd62f5e5ca..8e5a7f3d8a575 100644 --- a/kernel/time/Kconfig +++ b/kernel/time/Kconfig @@ -6,7 +6,7 @@ # Options selectable by arch Kconfig # Watchdog function for clocksources to detect instabilities -config CLOCKSOURCE_WATCHDOG +config HAVE_CLOCKSOURCE_WATCHDOG bool # Architecture has extra clocksource data @@ -181,5 +181,15 @@ config HIGH_RES_TIMERS hardware is not capable then this option only increases the size of the kernel image. +config CLOCKSOURCE_WATCHDOG + bool "Clocksource watchdog" + depends on HAVE_CLOCKSOURCE_WATCHDOG + default y + help + This option enables the watchdog function for clocksources. It is + used to detect instabilities in the currently selected clocksource. + + Say Y if you are unsure. + endmenu endif From f0b4ff231e611d584536d385120e33feb7c290dc Mon Sep 17 00:00:00 2001 From: Kyle Roeschley Date: Tue, 15 May 2018 15:59:28 -0500 Subject: [PATCH 79/84] Revert "mmc: core: enable CMD19 tuning for DDR50 mode" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 4324f6de6d2eb9b232410eb0d67bfafdde8ba711. The SD Physical Layer Simplified Specification Version 6.00 §4.2.4.5 says that tuning is only supported for SDR50 and SDR104 modes, not DDR50 mode. As a result, tuning always fails for DDR50 cards. Remove "support" for this in the kernel, as it only adds unnecessary error prints. This was tested with a variety of industrial and consumer microSD cards from Swissbit, Unirex, Patriot, SanDisk, and Link Depot. None of the cards were able to execute tuning in DDR50 mode. Signed-off-by: Kyle Roeschley Signed-off-by: Brad Mouring Acked-by: Tony Liechty Acked-by: Nathan Sullivan Natinst-ReviewBoard-ID: 236135 --- drivers/mmc/core/sd.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 4646b7a03db6b..975ae54312449 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -656,25 +656,9 @@ static int mmc_sd_init_uhs_card(struct mmc_card *card) * SDR104 mode SD-cards. Note that tuning is mandatory for SDR104. */ if (!mmc_host_is_spi(card->host) && - (card->host->ios.timing == MMC_TIMING_UHS_SDR50 || - card->host->ios.timing == MMC_TIMING_UHS_DDR50 || - card->host->ios.timing == MMC_TIMING_UHS_SDR104)) { + (card->sd_bus_speed == UHS_SDR50_BUS_SPEED || + card->sd_bus_speed == UHS_SDR104_BUS_SPEED)) err = mmc_execute_tuning(card); - - /* - * As SD Specifications Part1 Physical Layer Specification - * Version 3.01 says, CMD19 tuning is available for unlocked - * cards in transfer state of 1.8V signaling mode. The small - * difference between v3.00 and 3.01 spec means that CMD19 - * tuning is also available for DDR50 mode. - */ - if (err && card->host->ios.timing == MMC_TIMING_UHS_DDR50) { - pr_warn("%s: ddr50 tuning failed\n", - mmc_hostname(card->host)); - err = 0; - } - } - out: kfree(status); From 95be86ba1d40db2d4c951c069d73c02a5cb36910 Mon Sep 17 00:00:00 2001 From: Brad Mouring Date: Wed, 1 Mar 2017 13:43:12 -0600 Subject: [PATCH 80/84] Revert "time: Always make sure wall_to_monotonic isn't positive" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit e1d7ba8735551ed79c7a0463a042353574b96da3. NI has a use case that involves distributed networked Linux devices that need to share the same concept of time using a mechanism like IEEE-1588 or 802.1AS. The “master” device (ie. the device with the time all other devices will be synchronized to) is often a device that boots up set to the Posix Epoch, mainly because it lacks a battery-backed RTC. (note: the existence of an RTC does not prevent a device from booting up at or very near the Posix Epoch, it just greatly reduces the likelihood). If a slave device attempted to synchronize its CLOCK_REALTIME to that of the master – and the master’s time was < Epoch+slave uptime, that slave would not be able to synchronize. This use case is believed to be very common among embedded devices, especially those without RTCs. Long term: We (NI Timing & Sync) are planning on engaging with the upstream community to educate them on our use case and hopefully put a different solution in place which solves the original problem (preventing a negative boot time representation) while also allowing our use case to continue working as it did prior to the change we’re reverting. Once that happens, we can drop this revert. Signed-off-by: Brad Mouring Reported-by: Vineeth Acharya Tested-by: Rick Ratzel Natinst-CAR-ID: 629499 [bstreiff: reduced control flow in do_settimeofday64 due to unassigned 'ret'] Signed-off-by: Brandon Streiff --- kernel/time/timekeeping.c | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index a81beb3120386..9c569e4aecd35 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1297,7 +1297,6 @@ int do_settimeofday64(const struct timespec64 *ts) struct timekeeper *tk = &tk_core.timekeeper; struct timespec64 ts_delta, xt; unsigned long flags; - int ret = 0; if (!timespec64_valid_settod(ts)) return -EINVAL; @@ -1311,15 +1310,10 @@ int do_settimeofday64(const struct timespec64 *ts) ts_delta.tv_sec = ts->tv_sec - xt.tv_sec; ts_delta.tv_nsec = ts->tv_nsec - xt.tv_nsec; - if (timespec64_compare(&tk->wall_to_monotonic, &ts_delta) > 0) { - ret = -EINVAL; - goto out; - } - tk_set_wall_to_mono(tk, timespec64_sub(tk->wall_to_monotonic, ts_delta)); tk_set_xtime(tk, ts); -out: + timekeeping_update(tk, TK_CLEAR_NTP | TK_MIRROR | TK_CLOCK_WAS_SET); write_seqcount_end(&tk_core.seq); @@ -1328,10 +1322,9 @@ int do_settimeofday64(const struct timespec64 *ts) /* Signal hrtimers about time change */ clock_was_set(CLOCK_SET_WALL); - if (!ret) - audit_tk_injoffset(ts_delta); + audit_tk_injoffset(ts_delta); - return ret; + return 0; } EXPORT_SYMBOL(do_settimeofday64); @@ -1358,8 +1351,7 @@ static int timekeeping_inject_offset(const struct timespec64 *ts) /* Make sure the proposed value is valid */ tmp = timespec64_add(tk_xtime(tk), *ts); - if (timespec64_compare(&tk->wall_to_monotonic, ts) > 0 || - !timespec64_valid_settod(&tmp)) { + if (!timespec64_valid_settod(&tmp)) { ret = -EINVAL; goto error; } From 21a872f948f1bd14718758dcb3b3b6d1c8070f62 Mon Sep 17 00:00:00 2001 From: Josh Cartwright Date: Tue, 19 May 2015 19:24:43 -0500 Subject: [PATCH 81/84] nati_x86_64_defconfig: defconfig for NI x86_64-based targets Signed-off-by: Josh Cartwright [gratian: squashed unwieldy commits accumulated up to 5.10.59-rt52] Signed-off-by: Gratian Crisan --- arch/x86/configs/nati_x86_64_defconfig | 551 +++++++++++++++++++++++++ 1 file changed, 551 insertions(+) create mode 100644 arch/x86/configs/nati_x86_64_defconfig diff --git a/arch/x86/configs/nati_x86_64_defconfig b/arch/x86/configs/nati_x86_64_defconfig new file mode 100644 index 0000000000000..57a95eaedf62c --- /dev/null +++ b/arch/x86/configs/nati_x86_64_defconfig @@ -0,0 +1,551 @@ +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +CONFIG_AUDIT=y +# CONFIG_CONTEXT_TRACKING_FORCE is not set +CONFIG_HIGH_RES_TIMERS=y +# CONFIG_CLOCKSOURCE_WATCHDOG is not set +CONFIG_BPF_JIT=y +CONFIG_PREEMPT_RT=y +CONFIG_VIRT_CPU_ACCOUNTING_GEN=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +CONFIG_RCU_EXPERT=y +CONFIG_RCU_NOCB_CPU=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=18 +CONFIG_CGROUPS=y +CONFIG_CPUSETS=y +# CONFIG_PROC_PID_CPUSET is not set +CONFIG_CGROUP_CPUACCT=y +CONFIG_BLK_DEV_INITRD=y +# CONFIG_PCSPKR_PLATFORM is not set +CONFIG_KALLSYMS_ALL=y +CONFIG_EMBEDDED=y +# CONFIG_SLUB_DEBUG is not set +# CONFIG_COMPAT_BRK is not set +CONFIG_SMP=y +CONFIG_X86_X2APIC=y +# CONFIG_X86_EXTENDED_PLATFORM is not set +CONFIG_X86_INTEL_LPSS=y +# CONFIG_SCHED_OMIT_FRAME_POINTER is not set +CONFIG_PROCESSOR_SELECT=y +# CONFIG_CPU_SUP_CENTAUR is not set +# CONFIG_SCHED_MC is not set +CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y +CONFIG_MICROCODE_OLD_INTERFACE=y +CONFIG_X86_MSR=m +CONFIG_X86_CPUID=m +# CONFIG_X86_5LEVEL is not set +CONFIG_NUMA=y +# CONFIG_AMD_NUMA is not set +CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=1 +CONFIG_EFI=y +CONFIG_HZ_100=y +CONFIG_LEGACY_VSYSCALL_EMULATE=y +# CONFIG_MODIFY_LDT_SYSCALL is not set +# CONFIG_SUSPEND is not set +# CONFIG_ACPI_AC is not set +# CONFIG_ACPI_BATTERY is not set +CONFIG_ACPI_BUTTON=m +# CONFIG_ACPI_FAN is not set +# CONFIG_ACPI_THERMAL is not set +CONFIG_ACPI_APEI=y +CONFIG_ACPI_APEI_GHES=y +CONFIG_ACPI_APEI_MEMORY_FAILURE=y +CONFIG_INTEL_IDLE=y +CONFIG_IA32_EMULATION=y +CONFIG_EDD=y +CONFIG_EDD_OFF=y +CONFIG_DMI_SYSFS=m +CONFIG_EFI_VARS=y +CONFIG_EFI_RUNTIME=y +# CONFIG_VIRTUALIZATION is not set +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODVERSIONS=y +CONFIG_BLK_DEV_INTEGRITY=y +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set +CONFIG_KSM=y +CONFIG_DEFAULT_MMAP_MIN_ADDR=65536 +CONFIG_MEMORY_FAILURE=y +CONFIG_CLEANCACHE=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_TLS=m +CONFIG_TLS_DEVICE=y +CONFIG_XFRM_USER=m +CONFIG_NET_KEY=m +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_ADVANCED_ROUTER=y +CONFIG_IP_MULTIPLE_TABLES=y +CONFIG_IP_ROUTE_MULTIPATH=y +CONFIG_IP_ROUTE_VERBOSE=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +CONFIG_NET_IPIP=m +CONFIG_NET_IPGRE_DEMUX=m +CONFIG_NET_IPGRE=m +CONFIG_NET_IPGRE_BROADCAST=y +CONFIG_IP_MROUTE=y +CONFIG_IP_MROUTE_MULTIPLE_TABLES=y +CONFIG_IP_PIMSM_V1=y +CONFIG_IP_PIMSM_V2=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=m +CONFIG_INET_ESP_OFFLOAD=m +CONFIG_INET_IPCOMP=m +CONFIG_INET_DIAG=m +CONFIG_IPV6=m +CONFIG_INET6_AH=m +CONFIG_INET6_ESP=m +CONFIG_INET6_ESP_OFFLOAD=m +CONFIG_INET6_IPCOMP=m +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETFILTER=y +CONFIG_BRIDGE_NETFILTER=m +CONFIG_NF_CONNTRACK=m +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CONNTRACK_TIMESTAMP=y +CONFIG_NF_CONNTRACK_AMANDA=m +CONFIG_NF_CONNTRACK_FTP=m +CONFIG_NF_CONNTRACK_H323=m +CONFIG_NF_CONNTRACK_IRC=m +CONFIG_NF_CONNTRACK_NETBIOS_NS=m +CONFIG_NF_CONNTRACK_SNMP=m +CONFIG_NF_CONNTRACK_PPTP=m +CONFIG_NF_CONNTRACK_SANE=m +CONFIG_NF_CONNTRACK_SIP=m +CONFIG_NF_CONNTRACK_TFTP=m +CONFIG_NF_CT_NETLINK=m +CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m +CONFIG_NETFILTER_XT_TARGET_CONNMARK=m +CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m +CONFIG_NETFILTER_XT_TARGET_LOG=m +CONFIG_NETFILTER_XT_TARGET_MARK=m +CONFIG_NETFILTER_XT_TARGET_NFLOG=m +CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m +CONFIG_NETFILTER_XT_TARGET_TEE=m +CONFIG_NETFILTER_XT_TARGET_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m +CONFIG_NETFILTER_XT_MATCH_CLUSTER=m +CONFIG_NETFILTER_XT_MATCH_COMMENT=m +CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m +CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m +CONFIG_NETFILTER_XT_MATCH_CONNMARK=m +CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m +CONFIG_NETFILTER_XT_MATCH_CPU=m +CONFIG_NETFILTER_XT_MATCH_DCCP=m +CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m +CONFIG_NETFILTER_XT_MATCH_DSCP=m +CONFIG_NETFILTER_XT_MATCH_ESP=m +CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m +CONFIG_NETFILTER_XT_MATCH_HELPER=m +CONFIG_NETFILTER_XT_MATCH_IPRANGE=m +CONFIG_NETFILTER_XT_MATCH_LENGTH=m +CONFIG_NETFILTER_XT_MATCH_LIMIT=m +CONFIG_NETFILTER_XT_MATCH_MAC=m +CONFIG_NETFILTER_XT_MATCH_MARK=m +CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m +CONFIG_NETFILTER_XT_MATCH_OSF=m +CONFIG_NETFILTER_XT_MATCH_OWNER=m +CONFIG_NETFILTER_XT_MATCH_POLICY=m +CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m +CONFIG_NETFILTER_XT_MATCH_QUOTA=m +CONFIG_NETFILTER_XT_MATCH_RATEEST=m +CONFIG_NETFILTER_XT_MATCH_REALM=m +CONFIG_NETFILTER_XT_MATCH_RECENT=m +CONFIG_NETFILTER_XT_MATCH_SCTP=m +CONFIG_NETFILTER_XT_MATCH_STATE=m +CONFIG_NETFILTER_XT_MATCH_STATISTIC=m +CONFIG_NETFILTER_XT_MATCH_STRING=m +CONFIG_NETFILTER_XT_MATCH_TCPMSS=m +CONFIG_NETFILTER_XT_MATCH_TIME=m +CONFIG_NETFILTER_XT_MATCH_U32=m +CONFIG_IP_NF_IPTABLES=m +CONFIG_IP_NF_MATCH_AH=m +CONFIG_IP_NF_MATCH_ECN=m +CONFIG_IP_NF_MATCH_TTL=m +CONFIG_IP_NF_FILTER=m +CONFIG_IP_NF_TARGET_REJECT=m +CONFIG_IP_NF_MANGLE=m +CONFIG_IP_NF_TARGET_CLUSTERIP=m +CONFIG_IP_NF_TARGET_ECN=m +CONFIG_IP_NF_TARGET_TTL=m +CONFIG_IP_NF_RAW=m +CONFIG_IP_NF_ARPTABLES=m +CONFIG_IP_NF_ARPFILTER=m +CONFIG_IP_NF_ARP_MANGLE=m +CONFIG_IP6_NF_IPTABLES=m +CONFIG_IP6_NF_MATCH_AH=m +CONFIG_IP6_NF_MATCH_EUI64=m +CONFIG_IP6_NF_MATCH_FRAG=m +CONFIG_IP6_NF_MATCH_OPTS=m +CONFIG_IP6_NF_MATCH_HL=m +CONFIG_IP6_NF_MATCH_IPV6HEADER=m +CONFIG_IP6_NF_MATCH_MH=m +CONFIG_IP6_NF_MATCH_RT=m +CONFIG_IP6_NF_TARGET_HL=m +CONFIG_IP6_NF_FILTER=m +CONFIG_IP6_NF_TARGET_REJECT=m +CONFIG_IP6_NF_MANGLE=m +CONFIG_IP6_NF_RAW=m +CONFIG_L2TP=m +CONFIG_L2TP_DEBUGFS=m +CONFIG_L2TP_V3=y +CONFIG_L2TP_IP=m +CONFIG_L2TP_ETH=m +CONFIG_BRIDGE=m +CONFIG_VLAN_8021Q=m +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_MULTIQ=m +CONFIG_NET_SCH_NETEM=m +CONFIG_NET_CLS_BASIC=m +CONFIG_NET_CLS_U32=m +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_CMP=m +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_SKBEDIT=m +CONFIG_DCB=y +CONFIG_DNS_RESOLVER=y +CONFIG_BT=m +CONFIG_BT_HCIBTUSB=m +CONFIG_CFG80211=m +CONFIG_NL80211_TESTMODE=y +CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=m +CONFIG_MAC80211_LEDS=y +CONFIG_RFKILL=y +CONFIG_PCI=y +CONFIG_PCIEPORTBUS=y +CONFIG_HOTPLUG_PCI_PCIE=y +CONFIG_PCI_MSI=y +CONFIG_PCI_IOV=y +CONFIG_PCI_PRI=y +CONFIG_PCI_PASID=y +CONFIG_HOTPLUG_PCI=y +CONFIG_HOTPLUG_PCI_ACPI=y +CONFIG_UEVENT_HELPER=y +CONFIG_DEVTMPFS=y +CONFIG_DEVTMPFS_MOUNT=y +CONFIG_CONNECTOR=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_BLK_DEV_RAM=y +CONFIG_VIRTIO_BLK=m +CONFIG_BLK_DEV_NVME=y +CONFIG_NI_RT_FEATURES=y +CONFIG_NI_LED_PREFIX="nilrt" +CONFIG_NI_WATCHDOG=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_SCSI_SAS_ATA=y +CONFIG_SCSI_AACRAID=m +CONFIG_SCSI_MVSAS=m +CONFIG_SCSI_ARCMSR=m +CONFIG_SCSI_SYM53C8XX_2=m +CONFIG_SCSI_VIRTIO=m +CONFIG_ATA=y +CONFIG_SATA_AHCI=y +CONFIG_ATA_PIIX=y +CONFIG_SATA_MV=m +CONFIG_SATA_SIL=m +CONFIG_MD=y +CONFIG_BLK_DEV_DM=m +CONFIG_DM_CRYPT=m +CONFIG_FIREWIRE=m +CONFIG_FIREWIRE_OHCI=m +CONFIG_FIREWIRE_SBP2=m +CONFIG_FIREWIRE_NET=m +CONFIG_NETDEVICES=y +CONFIG_BONDING=m +CONFIG_WIREGUARD=m +CONFIG_TUN=y +CONFIG_VIRTIO_NET=m +# CONFIG_NET_VENDOR_3COM is not set +# CONFIG_NET_VENDOR_ADAPTEC is not set +# CONFIG_NET_VENDOR_ALTEON is not set +# CONFIG_NET_VENDOR_AMD is not set +# CONFIG_NET_VENDOR_ATHEROS is not set +CONFIG_TIGON3=m +# CONFIG_NET_VENDOR_BROCADE is not set +# CONFIG_NET_VENDOR_CHELSIO is not set +# CONFIG_NET_VENDOR_CISCO is not set +CONFIG_NET_TULIP=y +CONFIG_TULIP=m +# CONFIG_NET_VENDOR_DLINK is not set +# CONFIG_NET_VENDOR_EMULEX is not set +# CONFIG_NET_VENDOR_I825XX is not set +CONFIG_E100=m +CONFIG_E1000=m +CONFIG_E1000E=m +CONFIG_IGB=m +CONFIG_IGBVF=m +CONFIG_IXGB=m +CONFIG_IXGBE=m +# CONFIG_IXGBE_HWMON is not set +CONFIG_I40E=m +# CONFIG_NET_VENDOR_MARVELL is not set +CONFIG_MLX5_CORE=m +CONFIG_MLX5_CORE_EN=y +CONFIG_MLX5_IPSEC=y +CONFIG_MLX5_EN_IPSEC=y +CONFIG_MLX5_TLS=y +# CONFIG_NET_VENDOR_MICREL is not set +# CONFIG_NET_VENDOR_MICROCHIP is not set +# CONFIG_NET_VENDOR_MYRI is not set +# CONFIG_NET_VENDOR_NATSEMI is not set +# CONFIG_NET_VENDOR_NVIDIA is not set +# CONFIG_NET_VENDOR_OKI is not set +# CONFIG_NET_VENDOR_QLOGIC is not set +# CONFIG_NET_VENDOR_RDC is not set +# CONFIG_NET_VENDOR_REALTEK is not set +# CONFIG_NET_VENDOR_SEEQ is not set +# CONFIG_NET_VENDOR_SILAN is not set +# CONFIG_NET_VENDOR_SIS is not set +# CONFIG_NET_VENDOR_SMSC is not set +# CONFIG_NET_VENDOR_STMICRO is not set +# CONFIG_NET_VENDOR_SUN is not set +# CONFIG_NET_VENDOR_TEHUTI is not set +# CONFIG_NET_VENDOR_TI is not set +# CONFIG_NET_VENDOR_VIA is not set +# CONFIG_NET_VENDOR_WIZNET is not set +CONFIG_PHYLIB=y +CONFIG_MICROCHIP_PHY=m +CONFIG_PPP=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_USB_CATC=m +CONFIG_USB_KAWETH=m +CONFIG_USB_PEGASUS=m +CONFIG_USB_RTL8150=m +CONFIG_USB_RTL8152=m +CONFIG_USB_USBNET=m +CONFIG_USB_NET_CDC_EEM=m +CONFIG_USB_NET_SMSC75XX=m +CONFIG_USB_NET_SMSC95XX=m +CONFIG_USB_NET_PLUSB=m +CONFIG_USB_NET_RNDIS_HOST=m +CONFIG_USB_IPHETH=m +CONFIG_USB_SIERRA_NET=m +CONFIG_USB_VL600=m +CONFIG_USB_NET_CH9200=m +CONFIG_CARL9170=m +CONFIG_ATH6KL=m +CONFIG_ATH6KL_SDIO=m +CONFIG_ATH6KL_USB=m +CONFIG_ATH6KL_NI_BIOS_DOMAIN=y +CONFIG_ATH6KL_SILEX_FIRMWARE=y +CONFIG_AR5523=m +CONFIG_AT76C50X_USB=m +CONFIG_MT7601U=m +CONFIG_RT2X00=m +CONFIG_RT2500USB=m +CONFIG_RT73USB=m +CONFIG_RT2800USB=m +CONFIG_RT2800USB_RT53XX=y +CONFIG_RT2800USB_RT55XX=y +CONFIG_RT2800USB_UNKNOWN=y +CONFIG_RTL8187=m +CONFIG_RTL8192CU=m +CONFIG_USB_ZD1201=m +CONFIG_ZD1211RW=m +CONFIG_INPUT_MOUSEDEV=m +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_EVDEV=y +CONFIG_KEYBOARD_ATKBD=m +CONFIG_MOUSE_PS2=m +CONFIG_INPUT_TABLET=y +CONFIG_TABLET_SERIAL_WACOM4=m +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_USB_COMPOSITE=m +CONFIG_SERIO=m +# CONFIG_LEGACY_PTYS is not set +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=32 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_RSA=y +CONFIG_SERIAL_8250_DW=y +CONFIG_SERIAL_8250_NI16550=y +CONFIG_VIRTIO_CONSOLE=m +# CONFIG_HW_RANDOM_AMD is not set +# CONFIG_HW_RANDOM_VIA is not set +CONFIG_HW_RANDOM_VIRTIO=m +CONFIG_HPET=y +CONFIG_TCG_TPM=m +CONFIG_TCG_TIS=m +CONFIG_RANDOM_TRUST_CPU=y +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=m +CONFIG_I2C_I801=m +CONFIG_I2C_DESIGNWARE_PLATFORM=m +CONFIG_I2C_DESIGNWARE_PCI=m +CONFIG_SPI=y +CONFIG_SPI_PXA2XX=m +CONFIG_SPI_SPIDEV=m +CONFIG_PTP_1588_CLOCK=y +CONFIG_PINCTRL_BROXTON=y +CONFIG_SENSORS_CORETEMP=m +CONFIG_SENSORS_TMP421=m +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_CORE=y +CONFIG_SOFT_WATCHDOG=m +CONFIG_NIC7018_WDT=m +CONFIG_LPC_ICH=m +CONFIG_MEDIA_SUPPORT=m +CONFIG_MEDIA_SUBDRV_AUTOSELECT=y +CONFIG_MEDIA_USB_SUPPORT=y +CONFIG_USB_VIDEO_CLASS=m +# CONFIG_USB_GSPCA is not set +CONFIG_AGP=m +CONFIG_DRM=m +CONFIG_DRM_RADEON=m +CONFIG_DRM_I915=m +CONFIG_DRM_VMWGFX=m +CONFIG_DRM_VMWGFX_FBCON=y +CONFIG_DRM_BOCHS=m +CONFIG_DRM_VIRTIO_GPU=m +CONFIG_FB=y +CONFIG_FB_MODE_HELPERS=y +CONFIG_FB_EFI=y +CONFIG_LCD_CLASS_DEVICE=m +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_HIDRAW=y +CONFIG_HID_LOGITECH=m +CONFIG_HID_LOGITECH_DJ=m +CONFIG_LOGITECH_FF=y +CONFIG_LOGIRUMBLEPAD2_FF=y +CONFIG_LOGIG940_FF=y +CONFIG_HID_MICROSOFT=m +CONFIG_HID_MULTITOUCH=m +CONFIG_HID_PENMOUNT=m +CONFIG_USB_HIDDEV=y +CONFIG_USB=y +CONFIG_USB_DYNAMIC_MINORS=y +CONFIG_USB_XHCI_HCD=y +CONFIG_USB_XHCI_PLATFORM=y +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_UHCI_HCD=y +CONFIG_USB_STORAGE=y +CONFIG_USB_DWC3=m +CONFIG_USB_DWC3_GADGET=y +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_CH341=m +CONFIG_USB_SERIAL_WHITEHEAT=m +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP210X=m +CONFIG_USB_SERIAL_FTDI_SIO=m +CONFIG_USB_SERIAL_MOS7720=m +CONFIG_USB_SERIAL_MOS7840=m +CONFIG_USB_SERIAL_PL2303=m +CONFIG_USB_SERIAL_QUALCOMM=m +CONFIG_USB_SERIAL_SIERRAWIRELESS=m +CONFIG_NOP_USB_XCEIV=m +CONFIG_USB_GADGET=m +CONFIG_USB_ETH=m +# CONFIG_USB_ETH_RNDIS is not set +CONFIG_USB_ETH_EEM=y +CONFIG_MMC=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PCI=y +CONFIG_MMC_SDHCI_ACPI=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_NIC78BX=m +CONFIG_INFINIBAND=m +CONFIG_INFINIBAND_USER_ACCESS=m +CONFIG_MLX5_INFINIBAND=m +CONFIG_RTC_CLASS=y +CONFIG_DMADEVICES=y +CONFIG_UIO=m +CONFIG_UIO_PCI_GENERIC=m +CONFIG_VIRTIO_PCI=m +CONFIG_VIRTIO_BALLOON=m +CONFIG_VIRTIO_INPUT=m +CONFIG_VIRTIO_MMIO=m +CONFIG_INTEL_IOMMU=y +# CONFIG_INTEL_IOMMU_DEFAULT_ON is not set +CONFIG_IRQ_REMAP=y +CONFIG_USB4=m +CONFIG_DAX=m +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +# CONFIG_DNOTIFY is not set +CONFIG_FUSE_FS=m +CONFIG_OVERLAY_FS=y +CONFIG_ISO9660_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_NTFS_FS=m +CONFIG_PROC_KCORE=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_CONFIGFS_FS=y +CONFIG_EFIVAR_FS=y +CONFIG_SQUASHFS=y +CONFIG_SQUASHFS_XATTR=y +CONFIG_NFS_FS=m +CONFIG_NFS_V4=m +CONFIG_NFS_V4_1=y +CONFIG_CIFS=m +CONFIG_CIFS_XATTR=y +CONFIG_CIFS_POSIX=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=y +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=y +CONFIG_SECURITY=y +CONFIG_SECURITY_NETWORK=y +CONFIG_SECURITY_SELINUX=y +CONFIG_SECURITY_SELINUX_BOOTPARAM=y +CONFIG_DEFAULT_SECURITY_DAC=y +CONFIG_CRYPTO_PCRYPT=m +CONFIG_CRYPTO_CRYPTD=y +CONFIG_CRYPTO_CBC=y +CONFIG_CRYPTO_LRW=y +CONFIG_CRYPTO_XTS=y +CONFIG_CRYPTO_HMAC=y +CONFIG_CRYPTO_CRC32C_INTEL=y +CONFIG_CRYPTO_MD5=y +CONFIG_CRYPTO_SHA1=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_AES=y +CONFIG_CRYPTO_AES_NI_INTEL=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_USER_API_SKCIPHER=m +CONFIG_CRC7=m +CONFIG_LIBCRC32C=y +CONFIG_CRC8=m +CONFIG_PRINTK_TIME=y +CONFIG_FRAME_WARN=1024 +CONFIG_MAGIC_SYSRQ=y +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x0 +CONFIG_DEBUG_FS=y +CONFIG_PANIC_ON_OOPS=y +CONFIG_PANIC_TIMEOUT=30 +# CONFIG_SCHED_DEBUG is not set +# CONFIG_DEBUG_PREEMPT is not set +CONFIG_RCU_CPU_STALL_TIMEOUT=60 +# CONFIG_RCU_TRACE is not set +CONFIG_FUNCTION_TRACER=y +CONFIG_FUNCTION_PROFILER=y +CONFIG_FTRACE_SYSCALLS=y +CONFIG_TRACER_SNAPSHOT=y +CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP=y +# CONFIG_STRICT_DEVMEM is not set +# CONFIG_X86_VERBOSE_BOOTUP is not set +# CONFIG_EARLY_PRINTK is not set +CONFIG_IO_DELAY_0XED=y +CONFIG_UNWINDER_GUESS=y From 1bbee289f3352b8d92099d75887d31f71cd9cd2b Mon Sep 17 00:00:00 2001 From: Gratian Crisan Date: Thu, 26 Aug 2021 14:47:29 -0500 Subject: [PATCH 82/84] nati_x86_64_defconfig: disable CONFIG_SLUB_CPU_PARTIAL Per cpu partial SLUB caches enabled by CONFIG_SLUB_CPU_PARTIAL can introduce latency spikes on realtime systems due to the locks involved. Disable it. This also matches the recommendation in the help for this option "Typically one would choose no for a realtime system." Signed-off-by: Gratian Crisan --- arch/x86/configs/nati_x86_64_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/x86/configs/nati_x86_64_defconfig b/arch/x86/configs/nati_x86_64_defconfig index 57a95eaedf62c..cecf071190aef 100644 --- a/arch/x86/configs/nati_x86_64_defconfig +++ b/arch/x86/configs/nati_x86_64_defconfig @@ -24,6 +24,7 @@ CONFIG_KALLSYMS_ALL=y CONFIG_EMBEDDED=y # CONFIG_SLUB_DEBUG is not set # CONFIG_COMPAT_BRK is not set +# CONFIG_SLUB_CPU_PARTIAL is not set CONFIG_SMP=y CONFIG_X86_X2APIC=y # CONFIG_X86_EXTENDED_PLATFORM is not set From 82aae4ea80e7c091efe627d7c36deb498511f0d8 Mon Sep 17 00:00:00 2001 From: Bill Pittman Date: Mon, 30 Aug 2021 13:34:07 -0500 Subject: [PATCH 83/84] i2c-i801.c: Skip SPD initialization on cRIO-903x The cRIO-903x architecture is sort of special, we do not connect a dimm to the SPD because we use flash instead, but the SPD is still present because the processor expects it. However, enumerating and registering the SPD i2c bus is causing interrupt storms, so for now we skip the registration on all 903x devices. Fixes: 01590f3 ("i2c: i801: Instantiate SPD EEPROMs automatically") Signed-off-by: Bill Pittman Natinst-AZDO-ID: 1573148 (cherry picked from commit 4de019e72df37f49e08f352a835cf5779d57fac2) --- drivers/i2c/busses/i2c-i801.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 89ae78ef1a1cc..68ee5bd6ed0c5 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1278,6 +1278,8 @@ static void register_dell_lis3lv02d_i2c_device(struct i801_priv *priv) /* Register optional slaves */ static void i801_probe_optional_slaves(struct i801_priv *priv) { + const char *product; + /* Only register slaves on main SMBus channel */ if (priv->features & FEATURE_IDF) return; @@ -1297,11 +1299,17 @@ static void i801_probe_optional_slaves(struct i801_priv *priv) if (is_dell_system_with_lis3lv02d()) register_dell_lis3lv02d_i2c_device(priv); - /* Instantiate SPD EEPROMs unless the SMBus is multiplexed */ + /* Instantiate SPD EEPROMs unless the SMBus is multiplexed or it's a cRIO-903x */ + product = dmi_get_system_info(DMI_PRODUCT_NAME); + if(strncmp(product, "NI cRIO-903", 11)) { #if IS_ENABLED(CONFIG_I2C_MUX_GPIO) - if (!priv->mux_drvdata) + if (!priv->mux_drvdata) #endif - i2c_register_spd(&priv->adapter); + i2c_register_spd(&priv->adapter); + } + else { + dev_info(&priv->adapter.dev, "Found %s, skipping SPD registration.", product); + } } #else static void __init input_apanel_init(void) {} From 6d11aa9deea9f0ce0f312c5c2fd35836b38f9176 Mon Sep 17 00:00:00 2001 From: Gratian Crisan Date: Fri, 22 Oct 2021 16:02:56 -0500 Subject: [PATCH 84/84] nati_x86_64_defconfig: Re-generate for 5.15.y-rt No functional changes. Signed-off-by: Gratian Crisan --- arch/x86/configs/nati_x86_64_defconfig | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/arch/x86/configs/nati_x86_64_defconfig b/arch/x86/configs/nati_x86_64_defconfig index cecf071190aef..6afc2d7a930c6 100644 --- a/arch/x86/configs/nati_x86_64_defconfig +++ b/arch/x86/configs/nati_x86_64_defconfig @@ -56,11 +56,6 @@ CONFIG_ACPI_APEI_GHES=y CONFIG_ACPI_APEI_MEMORY_FAILURE=y CONFIG_INTEL_IDLE=y CONFIG_IA32_EMULATION=y -CONFIG_EDD=y -CONFIG_EDD_OFF=y -CONFIG_DMI_SYSFS=m -CONFIG_EFI_VARS=y -CONFIG_EFI_RUNTIME=y # CONFIG_VIRTUALIZATION is not set CONFIG_MODULES=y CONFIG_MODULE_UNLOAD=y @@ -234,6 +229,11 @@ CONFIG_UEVENT_HELPER=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y CONFIG_CONNECTOR=y +CONFIG_EDD=y +CONFIG_EDD_OFF=y +CONFIG_DMI_SYSFS=m +CONFIG_EFI_VARS=y +CONFIG_EFI_RUNTIME=y CONFIG_BLK_DEV_LOOP=y CONFIG_BLK_DEV_RAM=y CONFIG_VIRTIO_BLK=m @@ -392,7 +392,6 @@ CONFIG_I2C_DESIGNWARE_PCI=m CONFIG_SPI=y CONFIG_SPI_PXA2XX=m CONFIG_SPI_SPIDEV=m -CONFIG_PTP_1588_CLOCK=y CONFIG_PINCTRL_BROXTON=y CONFIG_SENSORS_CORETEMP=m CONFIG_SENSORS_TMP421=m @@ -405,15 +404,14 @@ CONFIG_MEDIA_SUPPORT=m CONFIG_MEDIA_SUBDRV_AUTOSELECT=y CONFIG_MEDIA_USB_SUPPORT=y CONFIG_USB_VIDEO_CLASS=m -# CONFIG_USB_GSPCA is not set CONFIG_AGP=m CONFIG_DRM=m CONFIG_DRM_RADEON=m CONFIG_DRM_I915=m CONFIG_DRM_VMWGFX=m CONFIG_DRM_VMWGFX_FBCON=y -CONFIG_DRM_BOCHS=m CONFIG_DRM_VIRTIO_GPU=m +CONFIG_DRM_BOCHS=m CONFIG_FB=y CONFIG_FB_MODE_HELPERS=y CONFIG_FB_EFI=y