From f7a2d73fe75c71941fb0a6b4d8fe7da8144f2c7b Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 6 Jun 2011 15:36:24 -0400 Subject: x86, efi: Fix argument types for SetVariable() The spec says this takes uint32 for attributes, not uintn. Signed-off-by: Matthew Garrett Link: http://lkml.kernel.org/r/1307388985-7852-1-git-send-email-mjg@redhat.com Signed-off-by: H. Peter Anvin --- arch/x86/platform/efi/efi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 0d3a4fa34560..f4f6de987b7b 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -122,7 +122,7 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, static efi_status_t virt_efi_set_variable(efi_char16_t *name, efi_guid_t *vendor, - unsigned long attr, + u32 attr, unsigned long data_size, void *data) { -- cgit v1.2.3 From 3b3702377c576f6624348c7c6fd113bfd934fbd7 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 6 Jun 2011 15:36:25 -0400 Subject: x86, efi: Add infrastructure for UEFI 2.0 runtime services We're currently missing support for any of the runtime service calls introduced with the UEFI 2.0 spec in 2006. Add the infrastructure for supporting them. Signed-off-by: Matthew Garrett Link: http://lkml.kernel.org/r/1307388985-7852-2-git-send-email-mjg@redhat.com Signed-off-by: H. Peter Anvin --- arch/x86/platform/efi/efi.c | 37 +++++++++++++++++++++++++++++++++++++ include/linux/efi.h | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index f4f6de987b7b..a0e42447de7c 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -131,6 +131,18 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, data_size, data); } +static efi_status_t virt_efi_query_variable_info(u32 attr, + u64 *storage_space, + u64 *remaining_space, + u64 *max_variable_size) +{ + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + return efi_call_virt4(query_variable_info, attr, storage_space, + remaining_space, max_variable_size); +} + static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) { return efi_call_virt1(get_next_high_mono_count, count); @@ -145,6 +157,28 @@ static void virt_efi_reset_system(int reset_type, data_size, data); } +static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, + unsigned long count, + unsigned long sg_list) +{ + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + return efi_call_virt3(update_capsule, capsules, count, sg_list); +} + +static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, + unsigned long count, + u64 *max_size, + int *reset_type) +{ + if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) + return EFI_UNSUPPORTED; + + return efi_call_virt4(query_capsule_caps, capsules, count, max_size, + reset_type); +} + static efi_status_t __init phys_efi_set_virtual_address_map( unsigned long memory_map_size, unsigned long descriptor_size, @@ -651,6 +685,9 @@ void __init efi_enter_virtual_mode(void) efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; efi.reset_system = virt_efi_reset_system; efi.set_virtual_address_map = NULL; + efi.query_variable_info = virt_efi_query_variable_info; + efi.update_capsule = virt_efi_update_capsule; + efi.query_capsule_caps = virt_efi_query_capsule_caps; if (__supported_pte_mask & _PAGE_NX) runtime_code_page_mkexec(); early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size); diff --git a/include/linux/efi.h b/include/linux/efi.h index 0758753a17a1..ec2572693925 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -101,6 +101,13 @@ typedef struct { u64 attribute; } efi_memory_desc_t; +typedef struct { + efi_guid_t guid; + u32 headersize; + u32 flags; + u32 imagesize; +} efi_capsule_header_t; + typedef int (*efi_freemem_callback_t) (u64 start, u64 end, void *arg); /* @@ -156,6 +163,9 @@ typedef struct { unsigned long set_variable; unsigned long get_next_high_mono_count; unsigned long reset_system; + unsigned long update_capsule; + unsigned long query_capsule_caps; + unsigned long query_variable_info; } efi_runtime_services_t; typedef efi_status_t efi_get_time_t (efi_time_t *tm, efi_time_cap_t *tc); @@ -177,6 +187,17 @@ typedef efi_status_t efi_set_virtual_address_map_t (unsigned long memory_map_siz unsigned long descriptor_size, u32 descriptor_version, efi_memory_desc_t *virtual_map); +typedef efi_status_t efi_query_variable_info_t(u32 attr, + u64 *storage_space, + u64 *remaining_space, + u64 *max_variable_size); +typedef efi_status_t efi_update_capsule_t(efi_capsule_header_t **capsules, + unsigned long count, + unsigned long sg_list); +typedef efi_status_t efi_query_capsule_caps_t(efi_capsule_header_t **capsules, + unsigned long count, + u64 *max_size, + int *reset_type); /* * EFI Configuration Table and GUID definitions @@ -218,6 +239,13 @@ typedef struct { #define EFI_SYSTEM_TABLE_SIGNATURE ((u64)0x5453595320494249ULL) +#define EFI_2_30_SYSTEM_TABLE_REVISION ((2 << 16) | (30)) +#define EFI_2_20_SYSTEM_TABLE_REVISION ((2 << 16) | (20)) +#define EFI_2_10_SYSTEM_TABLE_REVISION ((2 << 16) | (10)) +#define EFI_2_00_SYSTEM_TABLE_REVISION ((2 << 16) | (00)) +#define EFI_1_10_SYSTEM_TABLE_REVISION ((1 << 16) | (10)) +#define EFI_1_02_SYSTEM_TABLE_REVISION ((1 << 16) | (02)) + typedef struct { efi_table_hdr_t hdr; unsigned long fw_vendor; /* physical addr of CHAR16 vendor string */ @@ -250,6 +278,7 @@ struct efi_memory_map { */ extern struct efi { efi_system_table_t *systab; /* EFI system table */ + unsigned int runtime_version; /* Runtime services version */ unsigned long mps; /* MPS table */ unsigned long acpi; /* ACPI table (IA64 ext 0.71) */ unsigned long acpi20; /* ACPI table (ACPI 2.0) */ @@ -266,6 +295,9 @@ extern struct efi { efi_get_variable_t *get_variable; efi_get_next_variable_t *get_next_variable; efi_set_variable_t *set_variable; + efi_query_variable_info_t *query_variable_info; + efi_update_capsule_t *update_capsule; + efi_query_capsule_caps_t *query_capsule_caps; efi_get_next_high_mono_count_t *get_next_high_mono_count; efi_reset_system_t *reset_system; efi_set_virtual_address_map_t *set_virtual_address_map; -- cgit v1.2.3 From 00b30cf04a775b5292ab704f782394e36e25617d Mon Sep 17 00:00:00 2001 From: "cpw@sgi.com" Date: Tue, 21 Jun 2011 07:21:26 -0500 Subject: x86, UV: Fix smp_processor_id() use in a preemptable region Fix a call by tunables_write() to smp_processor_id() within a preemptable region. Call get_cpu()/put_cpu() around the region where the returned cpu number is actually used, which makes it non-preemptable. A DEBUG_PREEMPT warning is prevented. UV does not support cpu hotplug yet, but this is a step toward that ability as well. Signed-off-by: Cliff Wickman Link: http://lkml.kernel.org/r/20110621122242.086384966@sgi.com Signed-off-by: Ingo Molnar --- arch/x86/platform/uv/tlb_uv.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 68e467f69fec..34be65093309 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -1334,9 +1334,10 @@ static ssize_t tunables_write(struct file *file, const char __user *user, instr[count] = '\0'; - bcp = &per_cpu(bau_control, smp_processor_id()); - + cpu = get_cpu(); + bcp = &per_cpu(bau_control, cpu); ret = parse_tunables_write(bcp, instr, count); + put_cpu(); if (ret) return ret; -- cgit v1.2.3 From 9c9153db22870c3f37add83bea30500fcc268a73 Mon Sep 17 00:00:00 2001 From: "cpw@sgi.com" Date: Tue, 21 Jun 2011 07:21:28 -0500 Subject: x86, UV: Allow for non-consecutive sockets Fix for the topology in which there is a socket 1 on a blade with no socket 0. Only call make_per_cpu_thp() for present sockets. We have only seen this fail for internal configurations. Signed-off-by: Cliff Wickman Link: http://lkml.kernel.org/r/20110621122242.363757364@sgi.com Signed-off-by: Ingo Molnar --- arch/x86/platform/uv/tlb_uv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 34be65093309..7623b08eab1e 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -1752,10 +1752,10 @@ static int __init summarize_uvhub_sockets(int nuvhubs, sdp = &bdp->socket[socket]; if (scan_sock(sdp, bdp, &smaster, &hmaster)) return 1; + make_per_cpu_thp(smaster); } socket++; socket_mask = (socket_mask >> 1); - make_per_cpu_thp(smaster); } } return 0; -- cgit v1.2.3 From 485f07d349a3e5059413b886a3bc1996c4b3d14d Mon Sep 17 00:00:00 2001 From: "cpw@sgi.com" Date: Tue, 21 Jun 2011 07:21:29 -0500 Subject: x86, UV: Correct reset_with_ipi() Fix reset_with_ipi() to look up a cpu for a blade based on the distribution map being indexed by the potentially sparsely numbered pnode. This patch is critical to tlb shootdown on a partitioned UV system, or one with nonconsecutive blade numbers. The distribution map bits represent pnodes relative to the partition base pnode. Previous to this patch it had been assuming bits based on 0-based, consecutive blade ids. Signed-off-by: Cliff Wickman Link: http://lkml.kernel.org/r/20110621122242.497700003@sgi.com Signed-off-by: Ingo Molnar --- arch/x86/platform/uv/tlb_uv.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 7623b08eab1e..4b28e09e265e 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -296,14 +296,18 @@ static void bau_process_message(struct msg_desc *mdp, } /* - * Determine the first cpu on a uvhub. + * Determine the first cpu on a pnode. */ -static int uvhub_to_first_cpu(int uvhub) +static int pnode_to_first_cpu(int pnode, struct bau_control *smaster) { int cpu; - for_each_present_cpu(cpu) - if (uvhub == uv_cpu_to_blade_id(cpu)) + struct hub_and_pnode *hpp; + + for_each_present_cpu(cpu) { + hpp = &smaster->thp[cpu]; + if (pnode == hpp->pnode) return cpu; + } return -1; } @@ -366,23 +370,28 @@ static void do_reset(void *ptr) * Use IPI to get all target uvhubs to release resources held by * a given sending cpu number. */ -static void reset_with_ipi(struct bau_targ_hubmask *distribution, int sender) +static void reset_with_ipi(struct bau_targ_hubmask *distribution, + struct bau_control *bcp) { - int uvhub; + int pnode; + int apnode; int maskbits; cpumask_t mask; + int sender = bcp->cpu; + struct bau_control *smaster = bcp->socket_master; struct reset_args reset_args; reset_args.sender = sender; cpus_clear(mask); /* find a single cpu for each uvhub in this distribution mask */ maskbits = sizeof(struct bau_targ_hubmask) * BITSPERBYTE; - for (uvhub = 0; uvhub < maskbits; uvhub++) { + /* each bit is a pnode relative to the partition base pnode */ + for (pnode = 0; pnode < maskbits; pnode++) { int cpu; - if (!bau_uvhub_isset(uvhub, distribution)) + if (!bau_uvhub_isset(pnode, distribution)) continue; - /* find a cpu for this uvhub */ - cpu = uvhub_to_first_cpu(uvhub); + apnode = pnode + bcp->partition_base_pnode; + cpu = pnode_to_first_cpu(apnode, smaster); cpu_set(cpu, mask); } @@ -604,7 +613,7 @@ static void destination_plugged(struct bau_desc *bau_desc, quiesce_local_uvhub(hmaster); spin_lock(&hmaster->queue_lock); - reset_with_ipi(&bau_desc->distribution, bcp->cpu); + reset_with_ipi(&bau_desc->distribution, bcp); spin_unlock(&hmaster->queue_lock); end_uvhub_quiesce(hmaster); @@ -626,7 +635,7 @@ static void destination_timeout(struct bau_desc *bau_desc, quiesce_local_uvhub(hmaster); spin_lock(&hmaster->queue_lock); - reset_with_ipi(&bau_desc->distribution, bcp->cpu); + reset_with_ipi(&bau_desc->distribution, bcp); spin_unlock(&hmaster->queue_lock); end_uvhub_quiesce(hmaster); -- cgit v1.2.3 From a456eaab87461e33d94e748565eabd474283a475 Mon Sep 17 00:00:00 2001 From: "cpw@sgi.com" Date: Tue, 21 Jun 2011 07:21:30 -0500 Subject: x86, UV: Rename hubmask to pnmask Rename 'bau_targ_hubmask' to 'pnmask' for clarity. The BAU distribution bit mask is indexed by pnode number, not hub or blade number. This important fact is not clear while the mask is called a 'hubmask'. Signed-off-by: Cliff Wickman Link: http://lkml.kernel.org/r/20110621122242.630995969@sgi.com Signed-off-by: Ingo Molnar --- arch/x86/include/asm/uv/uv_bau.h | 12 ++++++------ arch/x86/platform/uv/tlb_uv.c | 5 ++--- 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/include/asm/uv/uv_bau.h b/arch/x86/include/asm/uv/uv_bau.h index 605e613b510c..fa6a48e264b3 100644 --- a/arch/x86/include/asm/uv/uv_bau.h +++ b/arch/x86/include/asm/uv/uv_bau.h @@ -183,7 +183,7 @@ * 'base_dest_nasid' field of the header corresponds to the * destination nodeID associated with that specified bit. */ -struct bau_targ_hubmask { +struct pnmask { unsigned long bits[BITS_TO_LONGS(UV_DISTRIBUTION_SIZE)]; }; @@ -314,7 +314,7 @@ struct bau_msg_header { * Should be 64 bytes */ struct bau_desc { - struct bau_targ_hubmask distribution; + struct pnmask distribution; /* * message template, consisting of header and payload: */ @@ -596,20 +596,20 @@ static inline void write_mmr_data_config(int pnode, unsigned long mr) uv_write_global_mmr64(pnode, UVH_BAU_DATA_CONFIG, mr); } -static inline int bau_uvhub_isset(int uvhub, struct bau_targ_hubmask *dstp) +static inline int bau_uvhub_isset(int uvhub, struct pnmask *dstp) { return constant_test_bit(uvhub, &dstp->bits[0]); } -static inline void bau_uvhub_set(int pnode, struct bau_targ_hubmask *dstp) +static inline void bau_uvhub_set(int pnode, struct pnmask *dstp) { __set_bit(pnode, &dstp->bits[0]); } -static inline void bau_uvhubs_clear(struct bau_targ_hubmask *dstp, +static inline void bau_uvhubs_clear(struct pnmask *dstp, int nbits) { bitmap_zero(&dstp->bits[0], nbits); } -static inline int bau_uvhub_weight(struct bau_targ_hubmask *dstp) +static inline int bau_uvhub_weight(struct pnmask *dstp) { return bitmap_weight((unsigned long *)&dstp->bits[0], UV_DISTRIBUTION_SIZE); diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 4b28e09e265e..191ef28ea173 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -370,8 +370,7 @@ static void do_reset(void *ptr) * Use IPI to get all target uvhubs to release resources held by * a given sending cpu number. */ -static void reset_with_ipi(struct bau_targ_hubmask *distribution, - struct bau_control *bcp) +static void reset_with_ipi(struct pnmask *distribution, struct bau_control *bcp) { int pnode; int apnode; @@ -384,7 +383,7 @@ static void reset_with_ipi(struct bau_targ_hubmask *distribution, reset_args.sender = sender; cpus_clear(mask); /* find a single cpu for each uvhub in this distribution mask */ - maskbits = sizeof(struct bau_targ_hubmask) * BITSPERBYTE; + maskbits = sizeof(struct pnmask) * BITSPERBYTE; /* each bit is a pnode relative to the partition base pnode */ for (pnode = 0; pnode < maskbits; pnode++) { int cpu; -- cgit v1.2.3 From 442d3924926c62741912d8a930220af253922007 Mon Sep 17 00:00:00 2001 From: "cpw@sgi.com" Date: Tue, 21 Jun 2011 07:21:31 -0500 Subject: x86, UV: Remove cpumask_t from the stack Remove the large stack-resident cpumask_t from reset_with_ipi()'s stack by allocating one per uvhub. Due to the limited size of the stack the potentially huge cpumask_t may cause stack overrun. We haven't seen it happen yet, but we need to make it a practice not to push such structures onto the stack. Signed-off-by: Cliff Wickman Reviewed-by: Pekka Enberg Link: http://lkml.kernel.org/r/20110621122242.832589130@sgi.com Signed-off-by: Ingo Molnar --- arch/x86/include/asm/uv/uv_bau.h | 1 + arch/x86/platform/uv/tlb_uv.c | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/include/asm/uv/uv_bau.h b/arch/x86/include/asm/uv/uv_bau.h index fa6a48e264b3..16ce58c6d252 100644 --- a/arch/x86/include/asm/uv/uv_bau.h +++ b/arch/x86/include/asm/uv/uv_bau.h @@ -488,6 +488,7 @@ struct bau_control { struct bau_control *uvhub_master; struct bau_control *socket_master; struct ptc_stats *statp; + cpumask_t *cpumask; unsigned long timeout_interval; unsigned long set_bau_on_time; atomic_t active_descriptor_count; diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 191ef28ea173..5265842b8d0b 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -375,13 +375,13 @@ static void reset_with_ipi(struct pnmask *distribution, struct bau_control *bcp) int pnode; int apnode; int maskbits; - cpumask_t mask; int sender = bcp->cpu; + cpumask_t *mask = bcp->uvhub_master->cpumask; struct bau_control *smaster = bcp->socket_master; struct reset_args reset_args; reset_args.sender = sender; - cpus_clear(mask); + cpus_clear(*mask); /* find a single cpu for each uvhub in this distribution mask */ maskbits = sizeof(struct pnmask) * BITSPERBYTE; /* each bit is a pnode relative to the partition base pnode */ @@ -391,11 +391,11 @@ static void reset_with_ipi(struct pnmask *distribution, struct bau_control *bcp) continue; apnode = pnode + bcp->partition_base_pnode; cpu = pnode_to_first_cpu(apnode, smaster); - cpu_set(cpu, mask); + cpu_set(cpu, *mask); } /* IPI all cpus; preemption is already disabled */ - smp_call_function_many(&mask, do_reset, (void *)&reset_args, 1); + smp_call_function_many(mask, do_reset, (void *)&reset_args, 1); return; } @@ -1695,6 +1695,16 @@ static void make_per_cpu_thp(struct bau_control *smaster) } } +/* + * Each uvhub is to get a local cpumask. + */ +static void make_per_hub_cpumask(struct bau_control *hmaster) +{ + int sz = sizeof(cpumask_t); + + hmaster->cpumask = kzalloc_node(sz, GFP_KERNEL, hmaster->osnode); +} + /* * Initialize all the per_cpu information for the cpu's on a given socket, * given what has been gathered into the socket_desc struct. @@ -1765,6 +1775,7 @@ static int __init summarize_uvhub_sockets(int nuvhubs, socket++; socket_mask = (socket_mask >> 1); } + make_per_hub_cpumask(hmaster); } return 0; } -- cgit v1.2.3 From bbd270e6f45a5ca30e1d3b6133c516a9dc9dd6c0 Mon Sep 17 00:00:00 2001 From: "cpw@sgi.com" Date: Tue, 21 Jun 2011 07:21:32 -0500 Subject: x86, UV: Correct failed topology memory leak Fix a memory leak in init_per_cpu() when the topology check fails. The leak should never occur on deployed systems. It would only occur in an unexpected topology that would make the BAU unuseable as a result. Signed-off-by: Cliff Wickman Link: http://lkml.kernel.org/r/20110621122242.981533045@sgi.com Signed-off-by: Ingo Molnar --- arch/x86/platform/uv/tlb_uv.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 5265842b8d0b..db8b915f54bc 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -1797,15 +1797,20 @@ static int __init init_per_cpu(int nuvhubs, int base_part_pnode) uvhub_mask = kzalloc((nuvhubs+7)/8, GFP_KERNEL); if (get_cpu_topology(base_part_pnode, uvhub_descs, uvhub_mask)) - return 1; + goto fail; if (summarize_uvhub_sockets(nuvhubs, uvhub_descs, uvhub_mask)) - return 1; + goto fail; kfree(uvhub_descs); kfree(uvhub_mask); init_per_cpu_tunables(); return 0; + +fail: + kfree(uvhub_descs); + kfree(uvhub_mask); + return 1; } /* -- cgit v1.2.3 From d80603c9d876efafd8b07469c891076de470e323 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Tue, 5 Jul 2011 12:22:18 +0100 Subject: x86, efi: Properly pre-initialize table pointers Consumers of the table pointers in struct efi check for EFI_INVALID_TABLE_ADDR to determine validity, hence these pointers should all be pre-initialized to this value (rather than zero). Noticed by the discrepancy between efivars' systab sysfs entry showing all tables (and their pointers) despite the code intending to only display the valid ones. No other bad effects known, but having the various table parsing routines bogusly access physical address zero is certainly not very desirable (even though they're unlikely to find anything useful there). Signed-off-by: Jan Beulich Link: http://lkml.kernel.org/r/4E13100A020000780004C256@nat28.tlf.novell.com Signed-off-by: Ingo Molnar --- arch/x86/platform/efi/efi.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index a0e42447de7c..11e766deb7b5 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -51,7 +51,17 @@ int efi_enabled; EXPORT_SYMBOL(efi_enabled); -struct efi efi; +struct efi __read_mostly efi = { + .mps = EFI_INVALID_TABLE_ADDR, + .acpi = EFI_INVALID_TABLE_ADDR, + .acpi20 = EFI_INVALID_TABLE_ADDR, + .smbios = EFI_INVALID_TABLE_ADDR, + .sal_systab = EFI_INVALID_TABLE_ADDR, + .boot_info = EFI_INVALID_TABLE_ADDR, + .hcdp = EFI_INVALID_TABLE_ADDR, + .uga = EFI_INVALID_TABLE_ADDR, + .uv_systab = EFI_INVALID_TABLE_ADDR, +}; EXPORT_SYMBOL(efi); struct efi_memory_map memmap; -- cgit v1.2.3 From f70d8ef4745349000dc599b6873a8b866289c694 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sat, 25 Jun 2011 17:34:08 +0100 Subject: x86, olpc: Add missing elements to device tree In response to new device tree code in the kernel, OLPC will start using it for probing of certain devices. However, some firmware fixes are needed to put the devicetree into a usable state. Retain compatibility with old firmware by fixing up the device tree at boot-time if it does not contain the new nodes/properties that we need for probing. This is the same approach taken on PPC platforms. Signed-off-by: Daniel Drake Link: http://lkml.kernel.org/r/1309019658-1712-2-git-send-email-dsd@laptop.org Acked-by: Grant Likely Acked-by: Andres Salomon Cc: devicetree-discuss@lists.ozlabs.org Signed-off-by: H. Peter Anvin --- arch/x86/platform/olpc/olpc_dt.c | 103 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/olpc/olpc_dt.c b/arch/x86/platform/olpc/olpc_dt.c index d39f63d017d2..d6ee92986920 100644 --- a/arch/x86/platform/olpc/olpc_dt.c +++ b/arch/x86/platform/olpc/olpc_dt.c @@ -165,6 +165,107 @@ static struct of_pdt_ops prom_olpc_ops __initdata = { .pkg2path = olpc_dt_pkg2path, }; +static phandle __init olpc_dt_finddevice(const char *path) +{ + phandle node; + const void *args[] = { path }; + void *res[] = { &node }; + + if (olpc_ofw("finddevice", args, res)) { + pr_err("olpc_dt: finddevice failed!\n"); + return 0; + } + + if ((s32) node == -1) + return 0; + + return node; +} + +static int __init olpc_dt_interpret(const char *words) +{ + int result; + const void *args[] = { words }; + void *res[] = { &result }; + + if (olpc_ofw("interpret", args, res)) { + pr_err("olpc_dt: interpret failed!\n"); + return -1; + } + + return result; +} + +/* + * Extract board revision directly from OFW device tree. + * We can't use olpc_platform_info because that hasn't been set up yet. + */ +static u32 __init olpc_dt_get_board_revision(void) +{ + phandle node; + __be32 rev; + int r; + + node = olpc_dt_finddevice("/"); + if (!node) + return 0; + + r = olpc_dt_getproperty(node, "board-revision-int", + (char *) &rev, sizeof(rev)); + if (r < 0) + return 0; + + return be32_to_cpu(rev); +} + +void __init olpc_dt_fixup(void) +{ + int r; + char buf[64]; + phandle node; + u32 board_rev; + + node = olpc_dt_finddevice("/battery@0"); + if (!node) + return; + + /* + * If the battery node has a compatible property, we are running a new + * enough firmware and don't have fixups to make. + */ + r = olpc_dt_getproperty(node, "compatible", buf, sizeof(buf)); + if (r > 0) + return; + + pr_info("PROM DT: Old firmware detected, applying fixes\n"); + + /* Add olpc,xo1-battery compatible marker to battery node */ + olpc_dt_interpret("\" /battery@0\" find-device" + " \" olpc,xo1-battery\" +compatible" + " device-end"); + + board_rev = olpc_dt_get_board_revision(); + if (!board_rev) + return; + + if (board_rev >= olpc_board_pre(0xd0)) { + /* XO-1.5: add dcon device */ + olpc_dt_interpret("\" /pci/display@1\" find-device" + " new-device" + " \" dcon\" device-name \" olpc,xo1-dcon\" +compatible" + " finish-device device-end"); + } else { + /* XO-1: add dcon device, mark RTC as olpc,xo1-rtc */ + olpc_dt_interpret("\" /pci/display@1,1\" find-device" + " new-device" + " \" dcon\" device-name \" olpc,xo1-dcon\" +compatible" + " finish-device device-end" + " \" /rtc\" find-device" + " \" olpc,xo1-rtc\" +compatible" + " device-end"); + } +} + void __init olpc_dt_build_devicetree(void) { phandle root; @@ -172,6 +273,8 @@ void __init olpc_dt_build_devicetree(void) if (!olpc_ofw_is_installed()) return; + olpc_dt_fixup(); + root = olpc_dt_getsibling(0); if (!root) { pr_err("PROM: unable to get root node from OFW!\n"); -- cgit v1.2.3 From 7a0d4fcf6d4b80b30503fd2701eeef1883e11404 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sat, 25 Jun 2011 17:34:09 +0100 Subject: x86, olpc: Move CS5536-related constants to cs5535.h Move these definitions into the relevant header file. This was requested in the review of the upcoming XO-1 suspend/resume code. Signed-off-by: Daniel Drake Link: http://lkml.kernel.org/r/1309019658-1712-3-git-send-email-dsd@laptop.org Acked-by: Andres Salomon Signed-off-by: H. Peter Anvin --- arch/x86/platform/olpc/olpc-xo1.c | 26 ++++++++------------------ include/linux/cs5535.h | 21 +++++++++++++++++++++ 2 files changed, 29 insertions(+), 18 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/olpc/olpc-xo1.c b/arch/x86/platform/olpc/olpc-xo1.c index ab81fb271760..a63e9488979f 100644 --- a/arch/x86/platform/olpc/olpc-xo1.c +++ b/arch/x86/platform/olpc/olpc-xo1.c @@ -12,6 +12,7 @@ * (at your option) any later version. */ +#include #include #include #include @@ -22,17 +23,6 @@ #define DRV_NAME "olpc-xo1" -/* PMC registers (PMS block) */ -#define PM_SCLK 0x10 -#define PM_IN_SLPCTL 0x20 -#define PM_WKXD 0x34 -#define PM_WKD 0x30 -#define PM_SSC 0x54 - -/* PM registers (ACPI block) */ -#define PM1_CNT 0x08 -#define PM_GPE0_STS 0x18 - static unsigned long acpi_base; static unsigned long pms_base; @@ -41,17 +31,17 @@ static void xo1_power_off(void) printk(KERN_INFO "OLPC XO-1 power off sequence...\n"); /* Enable all of these controls with 0 delay */ - outl(0x40000000, pms_base + PM_SCLK); - outl(0x40000000, pms_base + PM_IN_SLPCTL); - outl(0x40000000, pms_base + PM_WKXD); - outl(0x40000000, pms_base + PM_WKD); + outl(0x40000000, pms_base + CS5536_PM_SCLK); + outl(0x40000000, pms_base + CS5536_PM_IN_SLPCTL); + outl(0x40000000, pms_base + CS5536_PM_WKXD); + outl(0x40000000, pms_base + CS5536_PM_WKD); /* Clear status bits (possibly unnecessary) */ - outl(0x0002ffff, pms_base + PM_SSC); - outl(0xffffffff, acpi_base + PM_GPE0_STS); + outl(0x0002ffff, pms_base + CS5536_PM_SSC); + outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS); /* Write SLP_EN bit to start the machinery */ - outl(0x00002000, acpi_base + PM1_CNT); + outl(0x00002000, acpi_base + CS5536_PM1_CNT); } static int __devinit olpc_xo1_probe(struct platform_device *pdev) diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h index 6fe2114f8ad2..e46b8b0f9057 100644 --- a/include/linux/cs5535.h +++ b/include/linux/cs5535.h @@ -49,6 +49,27 @@ #define LBAR_ACPI_SIZE 0x40 #define LBAR_PMS_SIZE 0x80 +/* + * PMC registers (PMS block) + * It is only safe to access these registers as dword accesses. + * See CS5536 Specification Update erratas 17 & 18 + */ +#define CS5536_PM_SCLK 0x10 +#define CS5536_PM_IN_SLPCTL 0x20 +#define CS5536_PM_WKXD 0x34 +#define CS5536_PM_WKD 0x30 +#define CS5536_PM_SSC 0x54 + +/* + * PM registers (ACPI block) + * It is only safe to access these registers as dword accesses. + * See CS5536 Specification Update erratas 17 & 18 + */ +#define CS5536_PM1_STS 0x00 +#define CS5536_PM1_EN 0x02 +#define CS5536_PM1_CNT 0x08 +#define CS5536_PM_GPE0_STS 0x18 + /* VSA2 magic values */ #define VSA_VRC_INDEX 0xAC1C #define VSA_VRC_DATA 0xAC1E -- cgit v1.2.3 From a3128588b3c6be634a9013a375903e0b55668f0a Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sat, 25 Jun 2011 17:34:10 +0100 Subject: x86, olpc: Rename olpc-xo1 to olpc-xo1-pm Based on earlier review comments, we'll no longer try to stick all of our XO-1 goodies in a single driver. We'll split it into a power management driver, and an EC/SCI driver. As a first step, rename olpc-xo1 to olpc-xo1-pm, and make it builtin instead of modular. Signed-off-by: Daniel Drake Link: http://lkml.kernel.org/r/1309019658-1712-4-git-send-email-dsd@laptop.org Acked-by: Andres Salomon Signed-off-by: H. Peter Anvin --- arch/x86/Kconfig | 7 +- arch/x86/platform/olpc/Makefile | 2 +- arch/x86/platform/olpc/olpc-xo1-pm.c | 123 +++++++++++++++++++++++++++++++ arch/x86/platform/olpc/olpc-xo1.c | 136 ----------------------------------- 4 files changed, 128 insertions(+), 140 deletions(-) create mode 100644 arch/x86/platform/olpc/olpc-xo1-pm.c delete mode 100644 arch/x86/platform/olpc/olpc-xo1.c (limited to 'arch/x86/platform') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index da349723d411..29615ee688a5 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2073,11 +2073,12 @@ config OLPC Add support for detecting the unique features of the OLPC XO hardware. -config OLPC_XO1 - tristate "OLPC XO-1 support" +config OLPC_XO1_PM + bool "OLPC XO-1 Power Management" depends on OLPC && MFD_CS5535 + select MFD_CORE ---help--- - Add support for non-essential features of the OLPC XO-1 laptop. + Add support for poweroff of the OLPC XO-1 laptop. endif # X86_32 diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile index 81c5e2165c24..cd250387d4bb 100644 --- a/arch/x86/platform/olpc/Makefile +++ b/arch/x86/platform/olpc/Makefile @@ -1,2 +1,2 @@ obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o -obj-$(CONFIG_OLPC_XO1) += olpc-xo1.o +obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o diff --git a/arch/x86/platform/olpc/olpc-xo1-pm.c b/arch/x86/platform/olpc/olpc-xo1-pm.c new file mode 100644 index 000000000000..a2a59d36824d --- /dev/null +++ b/arch/x86/platform/olpc/olpc-xo1-pm.c @@ -0,0 +1,123 @@ +/* + * Support for power management features of the OLPC XO-1 laptop + * + * Copyright (C) 2010 Andres Salomon + * Copyright (C) 2010 One Laptop per Child + * Copyright (C) 2006 Red Hat, Inc. + * Copyright (C) 2006 Advanced Micro Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include + +#define DRV_NAME "olpc-xo1-pm" + +static unsigned long acpi_base; +static unsigned long pms_base; + +static void xo1_power_off(void) +{ + printk(KERN_INFO "OLPC XO-1 power off sequence...\n"); + + /* Enable all of these controls with 0 delay */ + outl(0x40000000, pms_base + CS5536_PM_SCLK); + outl(0x40000000, pms_base + CS5536_PM_IN_SLPCTL); + outl(0x40000000, pms_base + CS5536_PM_WKXD); + outl(0x40000000, pms_base + CS5536_PM_WKD); + + /* Clear status bits (possibly unnecessary) */ + outl(0x0002ffff, pms_base + CS5536_PM_SSC); + outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS); + + /* Write SLP_EN bit to start the machinery */ + outl(0x00002000, acpi_base + CS5536_PM1_CNT); +} + +static int __devinit xo1_pm_probe(struct platform_device *pdev) +{ + struct resource *res; + int err; + + /* don't run on non-XOs */ + if (!machine_is_olpc()) + return -ENODEV; + + err = mfd_cell_enable(pdev); + if (err) + return err; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "can't fetch device resource info\n"); + return -EIO; + } + if (strcmp(pdev->name, "cs5535-pms") == 0) + pms_base = res->start; + else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0) + acpi_base = res->start; + + /* If we have both addresses, we can override the poweroff hook */ + if (pms_base && acpi_base) { + pm_power_off = xo1_power_off; + printk(KERN_INFO "OLPC XO-1 support registered\n"); + } + + return 0; +} + +static int __devexit xo1_pm_remove(struct platform_device *pdev) +{ + mfd_cell_disable(pdev); + + if (strcmp(pdev->name, "cs5535-pms") == 0) + pms_base = 0; + else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0) + acpi_base = 0; + + pm_power_off = NULL; + return 0; +} + +static struct platform_driver cs5535_pms_driver = { + .driver = { + .name = "cs5535-pms", + .owner = THIS_MODULE, + }, + .probe = xo1_pm_probe, + .remove = __devexit_p(xo1_pm_remove), +}; + +static struct platform_driver cs5535_acpi_driver = { + .driver = { + .name = "olpc-xo1-pm-acpi", + .owner = THIS_MODULE, + }, + .probe = xo1_pm_probe, + .remove = __devexit_p(xo1_pm_remove), +}; + +static int __init xo1_pm_init(void) +{ + int r; + + r = platform_driver_register(&cs5535_pms_driver); + if (r) + return r; + + r = platform_driver_register(&cs5535_acpi_driver); + if (r) + platform_driver_unregister(&cs5535_pms_driver); + + return r; +} +arch_initcall(xo1_pm_init); diff --git a/arch/x86/platform/olpc/olpc-xo1.c b/arch/x86/platform/olpc/olpc-xo1.c deleted file mode 100644 index a63e9488979f..000000000000 --- a/arch/x86/platform/olpc/olpc-xo1.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Support for features of the OLPC XO-1 laptop - * - * Copyright (C) 2010 Andres Salomon - * Copyright (C) 2010 One Laptop per Child - * Copyright (C) 2006 Red Hat, Inc. - * Copyright (C) 2006 Advanced Micro Devices, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include -#include -#include -#include -#include - -#include -#include - -#define DRV_NAME "olpc-xo1" - -static unsigned long acpi_base; -static unsigned long pms_base; - -static void xo1_power_off(void) -{ - printk(KERN_INFO "OLPC XO-1 power off sequence...\n"); - - /* Enable all of these controls with 0 delay */ - outl(0x40000000, pms_base + CS5536_PM_SCLK); - outl(0x40000000, pms_base + CS5536_PM_IN_SLPCTL); - outl(0x40000000, pms_base + CS5536_PM_WKXD); - outl(0x40000000, pms_base + CS5536_PM_WKD); - - /* Clear status bits (possibly unnecessary) */ - outl(0x0002ffff, pms_base + CS5536_PM_SSC); - outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS); - - /* Write SLP_EN bit to start the machinery */ - outl(0x00002000, acpi_base + CS5536_PM1_CNT); -} - -static int __devinit olpc_xo1_probe(struct platform_device *pdev) -{ - struct resource *res; - int err; - - /* don't run on non-XOs */ - if (!machine_is_olpc()) - return -ENODEV; - - err = mfd_cell_enable(pdev); - if (err) - return err; - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!res) { - dev_err(&pdev->dev, "can't fetch device resource info\n"); - return -EIO; - } - if (strcmp(pdev->name, "cs5535-pms") == 0) - pms_base = res->start; - else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0) - acpi_base = res->start; - - /* If we have both addresses, we can override the poweroff hook */ - if (pms_base && acpi_base) { - pm_power_off = xo1_power_off; - printk(KERN_INFO "OLPC XO-1 support registered\n"); - } - - return 0; -} - -static int __devexit olpc_xo1_remove(struct platform_device *pdev) -{ - mfd_cell_disable(pdev); - - if (strcmp(pdev->name, "cs5535-pms") == 0) - pms_base = 0; - else if (strcmp(pdev->name, "olpc-xo1-pm-acpi") == 0) - acpi_base = 0; - - pm_power_off = NULL; - return 0; -} - -static struct platform_driver cs5535_pms_drv = { - .driver = { - .name = "cs5535-pms", - .owner = THIS_MODULE, - }, - .probe = olpc_xo1_probe, - .remove = __devexit_p(olpc_xo1_remove), -}; - -static struct platform_driver cs5535_acpi_drv = { - .driver = { - .name = "olpc-xo1-pm-acpi", - .owner = THIS_MODULE, - }, - .probe = olpc_xo1_probe, - .remove = __devexit_p(olpc_xo1_remove), -}; - -static int __init olpc_xo1_init(void) -{ - int r; - - r = platform_driver_register(&cs5535_pms_drv); - if (r) - return r; - - r = platform_driver_register(&cs5535_acpi_drv); - if (r) - platform_driver_unregister(&cs5535_pms_drv); - - return r; -} - -static void __exit olpc_xo1_exit(void) -{ - platform_driver_unregister(&cs5535_acpi_drv); - platform_driver_unregister(&cs5535_pms_drv); -} - -MODULE_AUTHOR("Daniel Drake "); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:cs5535-pms"); - -module_init(olpc_xo1_init); -module_exit(olpc_xo1_exit); -- cgit v1.2.3 From 97c4cb71c18fe045a763ff6681a8ebbbbbec0b2b Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sat, 25 Jun 2011 17:34:11 +0100 Subject: x86, olpc: Add XO-1 suspend/resume support Add code needed for basic suspend/resume of the XO-1 laptop. Based on earlier work by Jordan Crouse, Andres Salomon, and others. This patch incorporates all earlier feedback from Thomas Gleixner. To clarify a certain point (now more obvious in the code itself): On resume, OpenFirmware returns execution to Linux in protected mode with a kernel-compatible GDT already set up. The changes and simplifications suggested have all been included. Signed-off-by: Daniel Drake Link: http://lkml.kernel.org/r/1309019658-1712-5-git-send-email-dsd@laptop.org Acked-by: Andres Salomon Signed-off-by: H. Peter Anvin --- arch/x86/Kconfig | 4 +- arch/x86/include/asm/olpc.h | 15 ++++- arch/x86/platform/olpc/Makefile | 2 +- arch/x86/platform/olpc/olpc-xo1-pm.c | 92 ++++++++++++++++++++++++++ arch/x86/platform/olpc/xo1-wakeup.S | 124 +++++++++++++++++++++++++++++++++++ include/linux/cs5535.h | 3 + 6 files changed, 234 insertions(+), 6 deletions(-) create mode 100644 arch/x86/platform/olpc/xo1-wakeup.S (limited to 'arch/x86/platform') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 29615ee688a5..f473151ac991 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2075,10 +2075,10 @@ config OLPC config OLPC_XO1_PM bool "OLPC XO-1 Power Management" - depends on OLPC && MFD_CS5535 + depends on OLPC && MFD_CS5535 && PM_SLEEP select MFD_CORE ---help--- - Add support for poweroff of the OLPC XO-1 laptop. + Add support for poweroff and suspend of the OLPC XO-1 laptop. endif # X86_32 diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h index 5ca6801b75f3..10ea59594b71 100644 --- a/arch/x86/include/asm/olpc.h +++ b/arch/x86/include/asm/olpc.h @@ -76,6 +76,12 @@ static inline int olpc_has_dcon(void) #endif +#ifdef CONFIG_OLPC_XO1_PM +extern void do_olpc_suspend_lowlevel(void); +extern void olpc_xo1_pm_wakeup_set(u16 value); +extern void olpc_xo1_pm_wakeup_clear(u16 value); +#endif + extern int pci_olpc_init(void); /* EC related functions */ @@ -88,9 +94,12 @@ extern int olpc_ec_mask_unset(uint8_t bits); /* EC commands */ -#define EC_FIRMWARE_REV 0x08 -#define EC_WLAN_ENTER_RESET 0x35 -#define EC_WLAN_LEAVE_RESET 0x25 +#define EC_FIRMWARE_REV 0x08 +#define EC_WAKE_UP_WLAN 0x24 +#define EC_WLAN_LEAVE_RESET 0x25 +#define EC_SET_SCI_INHIBIT 0x32 +#define EC_SET_SCI_INHIBIT_RELEASE 0x34 +#define EC_WLAN_ENTER_RESET 0x35 /* SCI source values */ diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile index cd250387d4bb..1ae7bed89821 100644 --- a/arch/x86/platform/olpc/Makefile +++ b/arch/x86/platform/olpc/Makefile @@ -1,2 +1,2 @@ obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o -obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o +obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o xo1-wakeup.o diff --git a/arch/x86/platform/olpc/olpc-xo1-pm.c b/arch/x86/platform/olpc/olpc-xo1-pm.c index a2a59d36824d..6f3855a5a2f7 100644 --- a/arch/x86/platform/olpc/olpc-xo1-pm.c +++ b/arch/x86/platform/olpc/olpc-xo1-pm.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -25,6 +26,85 @@ static unsigned long acpi_base; static unsigned long pms_base; +static u16 wakeup_mask = CS5536_PM_PWRBTN; + +static struct { + unsigned long address; + unsigned short segment; +} ofw_bios_entry = { 0xF0000 + PAGE_OFFSET, __KERNEL_CS }; + +/* Set bits in the wakeup mask */ +void olpc_xo1_pm_wakeup_set(u16 value) +{ + wakeup_mask |= value; +} +EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_set); + +/* Clear bits in the wakeup mask */ +void olpc_xo1_pm_wakeup_clear(u16 value) +{ + wakeup_mask &= ~value; +} +EXPORT_SYMBOL_GPL(olpc_xo1_pm_wakeup_clear); + +static int xo1_power_state_enter(suspend_state_t pm_state) +{ + unsigned long saved_sci_mask; + int r; + + /* Only STR is supported */ + if (pm_state != PM_SUSPEND_MEM) + return -EINVAL; + + r = olpc_ec_cmd(EC_SET_SCI_INHIBIT, NULL, 0, NULL, 0); + if (r) + return r; + + /* + * Save SCI mask (this gets lost since PM1_EN is used as a mask for + * wakeup events, which is not necessarily the same event set) + */ + saved_sci_mask = inl(acpi_base + CS5536_PM1_STS); + saved_sci_mask &= 0xffff0000; + + /* Save CPU state */ + do_olpc_suspend_lowlevel(); + + /* Resume path starts here */ + + /* Restore SCI mask (using dword access to CS5536_PM1_EN) */ + outl(saved_sci_mask, acpi_base + CS5536_PM1_STS); + + /* Tell the EC to stop inhibiting SCIs */ + olpc_ec_cmd(EC_SET_SCI_INHIBIT_RELEASE, NULL, 0, NULL, 0); + + /* + * Tell the wireless module to restart USB communication. + * Must be done twice. + */ + olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0); + olpc_ec_cmd(EC_WAKE_UP_WLAN, NULL, 0, NULL, 0); + + return 0; +} + +asmlinkage int xo1_do_sleep(u8 sleep_state) +{ + void *pgd_addr = __va(read_cr3()); + + /* Program wakeup mask (using dword access to CS5536_PM1_EN) */ + outl(wakeup_mask << 16, acpi_base + CS5536_PM1_STS); + + __asm__("movl %0,%%eax" : : "r" (pgd_addr)); + __asm__("call *(%%edi); cld" + : : "D" (&ofw_bios_entry)); + __asm__("movb $0x34, %al\n\t" + "outb %al, $0x70\n\t" + "movb $0x30, %al\n\t" + "outb %al, $0x71\n\t"); + return 0; +} + static void xo1_power_off(void) { printk(KERN_INFO "OLPC XO-1 power off sequence...\n"); @@ -43,6 +123,17 @@ static void xo1_power_off(void) outl(0x00002000, acpi_base + CS5536_PM1_CNT); } +static int xo1_power_state_valid(suspend_state_t pm_state) +{ + /* suspend-to-RAM only */ + return pm_state == PM_SUSPEND_MEM; +} + +static const struct platform_suspend_ops xo1_suspend_ops = { + .valid = xo1_power_state_valid, + .enter = xo1_power_state_enter, +}; + static int __devinit xo1_pm_probe(struct platform_device *pdev) { struct resource *res; @@ -68,6 +159,7 @@ static int __devinit xo1_pm_probe(struct platform_device *pdev) /* If we have both addresses, we can override the poweroff hook */ if (pms_base && acpi_base) { + suspend_set_ops(&xo1_suspend_ops); pm_power_off = xo1_power_off; printk(KERN_INFO "OLPC XO-1 support registered\n"); } diff --git a/arch/x86/platform/olpc/xo1-wakeup.S b/arch/x86/platform/olpc/xo1-wakeup.S new file mode 100644 index 000000000000..948deb289753 --- /dev/null +++ b/arch/x86/platform/olpc/xo1-wakeup.S @@ -0,0 +1,124 @@ +.text +#include +#include +#include +#include + + .macro writepost,value + movb $0x34, %al + outb %al, $0x70 + movb $\value, %al + outb %al, $0x71 + .endm + +wakeup_start: + # OFW lands us here, running in protected mode, with a + # kernel-compatible GDT already setup. + + # Clear any dangerous flags + pushl $0 + popfl + + writepost 0x31 + + # Set up %cr3 + movl $initial_page_table - __PAGE_OFFSET, %eax + movl %eax, %cr3 + + movl saved_cr4, %eax + movl %eax, %cr4 + + movl saved_cr0, %eax + movl %eax, %cr0 + + # Control registers were modified, pipeline resync is needed + jmp 1f +1: + + movw $__KERNEL_DS, %ax + movw %ax, %ss + movw %ax, %ds + movw %ax, %es + movw %ax, %fs + movw %ax, %gs + + lgdt saved_gdt + lidt saved_idt + lldt saved_ldt + ljmp $(__KERNEL_CS),$1f +1: + movl %cr3, %eax + movl %eax, %cr3 + wbinvd + + # Go back to the return point + jmp ret_point + +save_registers: + sgdt saved_gdt + sidt saved_idt + sldt saved_ldt + + pushl %edx + movl %cr4, %edx + movl %edx, saved_cr4 + + movl %cr0, %edx + movl %edx, saved_cr0 + + popl %edx + + movl %ebx, saved_context_ebx + movl %ebp, saved_context_ebp + movl %esi, saved_context_esi + movl %edi, saved_context_edi + + pushfl + popl saved_context_eflags + + ret + +restore_registers: + movl saved_context_ebp, %ebp + movl saved_context_ebx, %ebx + movl saved_context_esi, %esi + movl saved_context_edi, %edi + + pushl saved_context_eflags + popfl + + ret + +ENTRY(do_olpc_suspend_lowlevel) + call save_processor_state + call save_registers + + # This is the stack context we want to remember + movl %esp, saved_context_esp + + pushl $3 + call xo1_do_sleep + + jmp wakeup_start + .p2align 4,,7 +ret_point: + movl saved_context_esp, %esp + + writepost 0x32 + + call restore_registers + call restore_processor_state + ret + +.data +saved_gdt: .long 0,0 +saved_idt: .long 0,0 +saved_ldt: .long 0 +saved_cr4: .long 0 +saved_cr0: .long 0 +saved_context_esp: .long 0 +saved_context_edi: .long 0 +saved_context_esi: .long 0 +saved_context_ebx: .long 0 +saved_context_ebp: .long 0 +saved_context_eflags: .long 0 diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h index e46b8b0f9057..2facf16f017b 100644 --- a/include/linux/cs5535.h +++ b/include/linux/cs5535.h @@ -70,6 +70,9 @@ #define CS5536_PM1_CNT 0x08 #define CS5536_PM_GPE0_STS 0x18 +/* CS5536_PM1_EN bits */ +#define CS5536_PM_PWRBTN (1 << 8) + /* VSA2 magic values */ #define VSA_VRC_INDEX 0xAC1C #define VSA_VRC_DATA 0xAC1E -- cgit v1.2.3 From 7feda8e9f35ebb0e9f90e03acb02280bc137f784 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sat, 25 Jun 2011 17:34:12 +0100 Subject: x86, olpc: Add XO-1 SCI driver and power button control The System Control Interrupt is used in the OLPC XO-1 to control various features of the laptop. Add the driver base and the power button functionality. This driver can't be built as a module, because functionality added in future patches means that some drivers need to know at boot-time whether SCI-based functionality is available. Signed-off-by: Daniel Drake Link: http://lkml.kernel.org/r/1309019658-1712-6-git-send-email-dsd@laptop.org Acked-by: Andres Salomon Signed-off-by: H. Peter Anvin --- arch/x86/Kconfig | 9 ++ arch/x86/platform/olpc/Makefile | 1 + arch/x86/platform/olpc/olpc-xo1-sci.c | 191 ++++++++++++++++++++++++++++++++++ include/linux/cs5535.h | 8 ++ 4 files changed, 209 insertions(+) create mode 100644 arch/x86/platform/olpc/olpc-xo1-sci.c (limited to 'arch/x86/platform') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f473151ac991..66b7b9d519d5 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2080,6 +2080,15 @@ config OLPC_XO1_PM ---help--- Add support for poweroff and suspend of the OLPC XO-1 laptop. +config OLPC_XO1_SCI + bool "OLPC XO-1 SCI extras" + depends on OLPC && OLPC_XO1_PM + select GPIO_CS5535 + select MFD_CORE + ---help--- + Add support for SCI-based features of the OLPC XO-1 laptop: + - Power button + endif # X86_32 config AMD_NB diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile index 1ae7bed89821..1ec5ade97f44 100644 --- a/arch/x86/platform/olpc/Makefile +++ b/arch/x86/platform/olpc/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o xo1-wakeup.o +obj-$(CONFIG_OLPC_XO1_SCI) += olpc-xo1-sci.o diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c new file mode 100644 index 000000000000..8fbf961dae89 --- /dev/null +++ b/arch/x86/platform/olpc/olpc-xo1-sci.c @@ -0,0 +1,191 @@ +/* + * Support for OLPC XO-1 System Control Interrupts (SCI) + * + * Copyright (C) 2010 One Laptop per Child + * Copyright (C) 2006 Red Hat, Inc. + * Copyright (C) 2006 Advanced Micro Devices, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define DRV_NAME "olpc-xo1-sci" +#define PFX DRV_NAME ": " + +static unsigned long acpi_base; +static struct input_dev *power_button_idev; +static int sci_irq; + +static irqreturn_t xo1_sci_intr(int irq, void *dev_id) +{ + struct platform_device *pdev = dev_id; + u32 sts; + u32 gpe; + + sts = inl(acpi_base + CS5536_PM1_STS); + outl(sts | 0xffff, acpi_base + CS5536_PM1_STS); + + gpe = inl(acpi_base + CS5536_PM_GPE0_STS); + outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS); + + dev_dbg(&pdev->dev, "sts %x gpe %x\n", sts, gpe); + + if (sts & CS5536_PWRBTN_FLAG && !(sts & CS5536_WAK_FLAG)) { + input_report_key(power_button_idev, KEY_POWER, 1); + input_sync(power_button_idev); + input_report_key(power_button_idev, KEY_POWER, 0); + input_sync(power_button_idev); + } + + return IRQ_HANDLED; +} + +static int xo1_sci_suspend(struct platform_device *pdev, pm_message_t state) +{ + if (device_may_wakeup(&power_button_idev->dev)) + olpc_xo1_pm_wakeup_set(CS5536_PM_PWRBTN); + else + olpc_xo1_pm_wakeup_clear(CS5536_PM_PWRBTN); + return 0; +} + +static int __devinit setup_sci_interrupt(struct platform_device *pdev) +{ + u32 lo, hi; + u32 sts; + int r; + + rdmsr(0x51400020, lo, hi); + sci_irq = (lo >> 20) & 15; + + if (sci_irq) { + dev_info(&pdev->dev, "SCI is mapped to IRQ %d\n", sci_irq); + } else { + /* Zero means masked */ + dev_info(&pdev->dev, "SCI unmapped. Mapping to IRQ 3\n"); + sci_irq = 3; + lo |= 0x00300000; + wrmsrl(0x51400020, lo); + } + + /* Select level triggered in PIC */ + if (sci_irq < 8) { + lo = inb(CS5536_PIC_INT_SEL1); + lo |= 1 << sci_irq; + outb(lo, CS5536_PIC_INT_SEL1); + } else { + lo = inb(CS5536_PIC_INT_SEL2); + lo |= 1 << (sci_irq - 8); + outb(lo, CS5536_PIC_INT_SEL2); + } + + /* Enable SCI from power button, and clear pending interrupts */ + sts = inl(acpi_base + CS5536_PM1_STS); + outl((CS5536_PM_PWRBTN << 16) | 0xffff, acpi_base + CS5536_PM1_STS); + + r = request_irq(sci_irq, xo1_sci_intr, 0, DRV_NAME, pdev); + if (r) + dev_err(&pdev->dev, "can't request interrupt\n"); + + return r; +} + +static int __devinit setup_power_button(struct platform_device *pdev) +{ + int r; + + power_button_idev = input_allocate_device(); + if (!power_button_idev) + return -ENOMEM; + + power_button_idev->name = "Power Button"; + power_button_idev->phys = DRV_NAME "/input0"; + set_bit(EV_KEY, power_button_idev->evbit); + set_bit(KEY_POWER, power_button_idev->keybit); + + power_button_idev->dev.parent = &pdev->dev; + device_init_wakeup(&power_button_idev->dev, 1); + + r = input_register_device(power_button_idev); + if (r) { + dev_err(&pdev->dev, "failed to register power button: %d\n", r); + input_free_device(power_button_idev); + } + + return r; +} + +static void free_power_button(void) +{ + input_unregister_device(power_button_idev); + input_free_device(power_button_idev); +} + +static int __devinit xo1_sci_probe(struct platform_device *pdev) +{ + struct resource *res; + int r; + + /* don't run on non-XOs */ + if (!machine_is_olpc()) + return -ENODEV; + + r = mfd_cell_enable(pdev); + if (r) + return r; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "can't fetch device resource info\n"); + return -EIO; + } + acpi_base = res->start; + + r = setup_power_button(pdev); + if (r) + return r; + + r = setup_sci_interrupt(pdev); + if (r) + free_power_button(); + + return r; +} + +static int __devexit xo1_sci_remove(struct platform_device *pdev) +{ + mfd_cell_disable(pdev); + free_irq(sci_irq, pdev); + free_power_button(); + acpi_base = 0; + return 0; +} + +static struct platform_driver xo1_sci_driver = { + .driver = { + .name = "olpc-xo1-sci-acpi", + }, + .probe = xo1_sci_probe, + .remove = __devexit_p(xo1_sci_remove), + .suspend = xo1_sci_suspend, +}; + +static int __init xo1_sci_init(void) +{ + return platform_driver_register(&xo1_sci_driver); +} +arch_initcall(xo1_sci_init); diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h index 2facf16f017b..6f78235cb905 100644 --- a/include/linux/cs5535.h +++ b/include/linux/cs5535.h @@ -43,6 +43,10 @@ #define MSR_GX_GLD_MSR_CONFIG 0xC0002001 #define MSR_GX_MSR_PADSEL 0xC0002011 +/* PIC registers */ +#define CS5536_PIC_INT_SEL1 0x4d0 +#define CS5536_PIC_INT_SEL2 0x4d1 + /* resource sizes */ #define LBAR_GPIO_SIZE 0xFF #define LBAR_MFGPT_SIZE 0x40 @@ -70,6 +74,10 @@ #define CS5536_PM1_CNT 0x08 #define CS5536_PM_GPE0_STS 0x18 +/* CS5536_PM1_STS bits */ +#define CS5536_WAK_FLAG (1 << 15) +#define CS5536_PWRBTN_FLAG (1 << 8) + /* CS5536_PM1_EN bits */ #define CS5536_PM_PWRBTN (1 << 8) -- cgit v1.2.3 From bc4ecd5a5efc2435e6debfb7b279a15ae96697fd Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sat, 25 Jun 2011 17:34:13 +0100 Subject: x86, olpc: EC SCI wakeup mask functionality Update the EC SCI masks with recent additions. Add functions to query SCI events and set the wakeup mask, to be used by followup patches. Add functions to tweak an event mask used to select certain EC events as a system wakeup source. Also add a function to determine if EC wakeup functionality is available, as this depends on child drivers (different for each laptop model) to configure the SCI interrupt. Signed-off-by: Daniel Drake Link: http://lkml.kernel.org/r/1309019658-1712-7-git-send-email-dsd@laptop.org Acked-by: Andres Salomon Signed-off-by: H. Peter Anvin --- arch/x86/include/asm/olpc.h | 31 +++++++++++++--- arch/x86/platform/olpc/olpc.c | 86 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 6 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h index 10ea59594b71..0e56d01907fe 100644 --- a/arch/x86/include/asm/olpc.h +++ b/arch/x86/include/asm/olpc.h @@ -13,6 +13,7 @@ struct olpc_platform_t { #define OLPC_F_PRESENT 0x01 #define OLPC_F_DCON 0x02 +#define OLPC_F_EC_WIDE_SCI 0x04 #ifdef CONFIG_OLPC @@ -62,6 +63,13 @@ static inline int olpc_board_at_least(uint32_t rev) return olpc_platform_info.boardrev >= rev; } +extern void olpc_ec_wakeup_set(u16 value); +extern void olpc_ec_wakeup_clear(u16 value); +extern bool olpc_ec_wakeup_available(void); + +extern int olpc_ec_mask_write(u16 bits); +extern int olpc_ec_sci_query(u16 *sci_value); + #else static inline int machine_is_olpc(void) @@ -74,6 +82,14 @@ static inline int olpc_has_dcon(void) return 0; } +static inline void olpc_ec_wakeup_set(u16 value) { } +static inline void olpc_ec_wakeup_clear(u16 value) { } + +static inline bool olpc_ec_wakeup_available(void) +{ + return false; +} + #endif #ifdef CONFIG_OLPC_XO1_PM @@ -89,17 +105,18 @@ extern int pci_olpc_init(void); extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen, unsigned char *outbuf, size_t outlen); -extern int olpc_ec_mask_set(uint8_t bits); -extern int olpc_ec_mask_unset(uint8_t bits); - /* EC commands */ #define EC_FIRMWARE_REV 0x08 +#define EC_WRITE_SCI_MASK 0x1b #define EC_WAKE_UP_WLAN 0x24 #define EC_WLAN_LEAVE_RESET 0x25 #define EC_SET_SCI_INHIBIT 0x32 #define EC_SET_SCI_INHIBIT_RELEASE 0x34 #define EC_WLAN_ENTER_RESET 0x35 +#define EC_WRITE_EXT_SCI_MASK 0x38 +#define EC_SCI_QUERY 0x84 +#define EC_EXT_SCI_QUERY 0x85 /* SCI source values */ @@ -108,10 +125,12 @@ extern int olpc_ec_mask_unset(uint8_t bits); #define EC_SCI_SRC_BATTERY 0x02 #define EC_SCI_SRC_BATSOC 0x04 #define EC_SCI_SRC_BATERR 0x08 -#define EC_SCI_SRC_EBOOK 0x10 -#define EC_SCI_SRC_WLAN 0x20 +#define EC_SCI_SRC_EBOOK 0x10 /* XO-1 only */ +#define EC_SCI_SRC_WLAN 0x20 /* XO-1 only */ #define EC_SCI_SRC_ACPWR 0x40 -#define EC_SCI_SRC_ALL 0x7F +#define EC_SCI_SRC_BATCRIT 0x80 +#define EC_SCI_SRC_GPWAKE 0x100 /* XO-1.5 only */ +#define EC_SCI_SRC_ALL 0x1FF /* GPIO assignments */ diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c index 0060fd59ea00..72fd041762fc 100644 --- a/arch/x86/platform/olpc/olpc.c +++ b/arch/x86/platform/olpc/olpc.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -30,6 +31,9 @@ EXPORT_SYMBOL_GPL(olpc_platform_info); static DEFINE_SPINLOCK(ec_lock); +/* EC event mask to be applied during suspend (defining wakeup sources). */ +static u16 ec_wakeup_mask; + /* what the timeout *should* be (in ms) */ #define EC_BASE_TIMEOUT 20 @@ -188,6 +192,79 @@ err: } EXPORT_SYMBOL_GPL(olpc_ec_cmd); +void olpc_ec_wakeup_set(u16 value) +{ + ec_wakeup_mask |= value; +} +EXPORT_SYMBOL_GPL(olpc_ec_wakeup_set); + +void olpc_ec_wakeup_clear(u16 value) +{ + ec_wakeup_mask &= ~value; +} +EXPORT_SYMBOL_GPL(olpc_ec_wakeup_clear); + +/* + * Returns true if the compile and runtime configurations allow for EC events + * to wake the system. + */ +bool olpc_ec_wakeup_available(void) +{ + if (!machine_is_olpc()) + return false; + + /* + * XO-1 EC wakeups are available when olpc-xo1-sci driver is + * compiled in + */ +#ifdef CONFIG_OLPC_XO1_SCI + if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) /* XO-1 */ + return true; +#endif + + return false; +} +EXPORT_SYMBOL_GPL(olpc_ec_wakeup_available); + +int olpc_ec_mask_write(u16 bits) +{ + if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) { + __be16 ec_word = cpu_to_be16(bits); + return olpc_ec_cmd(EC_WRITE_EXT_SCI_MASK, (void *) &ec_word, 2, + NULL, 0); + } else { + unsigned char ec_byte = bits & 0xff; + return olpc_ec_cmd(EC_WRITE_SCI_MASK, &ec_byte, 1, NULL, 0); + } +} +EXPORT_SYMBOL_GPL(olpc_ec_mask_write); + +int olpc_ec_sci_query(u16 *sci_value) +{ + int ret; + + if (olpc_platform_info.flags & OLPC_F_EC_WIDE_SCI) { + __be16 ec_word; + ret = olpc_ec_cmd(EC_EXT_SCI_QUERY, + NULL, 0, (void *) &ec_word, 2); + if (ret == 0) + *sci_value = be16_to_cpu(ec_word); + } else { + unsigned char ec_byte; + ret = olpc_ec_cmd(EC_SCI_QUERY, NULL, 0, &ec_byte, 1); + if (ret == 0) + *sci_value = ec_byte; + } + + return ret; +} +EXPORT_SYMBOL_GPL(olpc_ec_sci_query); + +static int olpc_ec_suspend(void) +{ + return olpc_ec_mask_write(ec_wakeup_mask); +} + static bool __init check_ofw_architecture(struct device_node *root) { const char *olpc_arch; @@ -242,6 +319,10 @@ static int __init add_xo1_platform_devices(void) return 0; } +static struct syscore_ops olpc_syscore_ops = { + .suspend = olpc_ec_suspend, +}; + static int __init olpc_init(void) { int r = 0; @@ -266,6 +347,9 @@ static int __init olpc_init(void) !cs5535_has_vsa2()) x86_init.pci.arch_init = pci_olpc_init; #endif + /* EC version 0x5f adds support for wide SCI mask */ + if (olpc_platform_info.ecver >= 0x5f) + olpc_platform_info.flags |= OLPC_F_EC_WIDE_SCI; printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n", ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "", @@ -278,6 +362,8 @@ static int __init olpc_init(void) return r; } + register_syscore_ops(&olpc_syscore_ops); + return 0; } -- cgit v1.2.3 From 7bc74b3df73776fe06f3df9fafd2d2698e6ca28a Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sat, 25 Jun 2011 17:34:14 +0100 Subject: x86, olpc-xo1-sci: Add GPE handler and ebook switch functionality The EC in the OLPC XO-1 delivers GPE events to provide various notifications. Add the basic code for GPE/EC event processing and enable the ebook switch, which can be used as a wakeup source. Signed-off-by: Daniel Drake Link: http://lkml.kernel.org/r/1309019658-1712-8-git-send-email-dsd@laptop.org Acked-by: Andres Salomon Signed-off-by: H. Peter Anvin --- arch/x86/Kconfig | 2 + arch/x86/include/asm/olpc.h | 5 +- arch/x86/platform/olpc/olpc-xo1-sci.c | 183 +++++++++++++++++++++++++++++++++- include/linux/cs5535.h | 22 ++++ 4 files changed, 209 insertions(+), 3 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 66b7b9d519d5..88889106ac9b 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2087,7 +2087,9 @@ config OLPC_XO1_SCI select MFD_CORE ---help--- Add support for SCI-based features of the OLPC XO-1 laptop: + - EC-driven system wakeups - Power button + - Ebook switch endif # X86_32 diff --git a/arch/x86/include/asm/olpc.h b/arch/x86/include/asm/olpc.h index 0e56d01907fe..87bdbca72f94 100644 --- a/arch/x86/include/asm/olpc.h +++ b/arch/x86/include/asm/olpc.h @@ -111,6 +111,7 @@ extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen, #define EC_WRITE_SCI_MASK 0x1b #define EC_WAKE_UP_WLAN 0x24 #define EC_WLAN_LEAVE_RESET 0x25 +#define EC_READ_EB_MODE 0x2a #define EC_SET_SCI_INHIBIT 0x32 #define EC_SET_SCI_INHIBIT_RELEASE 0x34 #define EC_WLAN_ENTER_RESET 0x35 @@ -144,7 +145,7 @@ extern int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen, #define OLPC_GPIO_SMB_CLK 14 #define OLPC_GPIO_SMB_DATA 15 #define OLPC_GPIO_WORKAUX geode_gpio(24) -#define OLPC_GPIO_LID geode_gpio(26) -#define OLPC_GPIO_ECSCI geode_gpio(27) +#define OLPC_GPIO_LID 26 +#define OLPC_GPIO_ECSCI 27 #endif /* _ASM_X86_OLPC_H */ diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c index 8fbf961dae89..63f50506078b 100644 --- a/arch/x86/platform/olpc/olpc-xo1-sci.c +++ b/arch/x86/platform/olpc/olpc-xo1-sci.c @@ -12,12 +12,15 @@ */ #include +#include +#include #include #include #include #include #include #include +#include #include #include @@ -28,8 +31,60 @@ static unsigned long acpi_base; static struct input_dev *power_button_idev; +static struct input_dev *ebook_switch_idev; + static int sci_irq; +/* Report current ebook switch state through input layer */ +static void send_ebook_state(void) +{ + unsigned char state; + + if (olpc_ec_cmd(EC_READ_EB_MODE, NULL, 0, &state, 1)) { + pr_err(PFX "failed to get ebook state\n"); + return; + } + + input_report_switch(ebook_switch_idev, SW_TABLET_MODE, state); + input_sync(ebook_switch_idev); +} + +/* + * Process all items in the EC's SCI queue. + * + * This is handled in a workqueue because olpc_ec_cmd can be slow (and + * can even timeout). + * + * If propagate_events is false, the queue is drained without events being + * generated for the interrupts. + */ +static void process_sci_queue(bool propagate_events) +{ + int r; + u16 data; + + do { + r = olpc_ec_sci_query(&data); + if (r || !data) + break; + + pr_debug(PFX "SCI 0x%x received\n", data); + + if (data == EC_SCI_SRC_EBOOK && propagate_events) + send_ebook_state(); + } while (data); + + if (r) + pr_err(PFX "Failed to clear SCI queue"); +} + +static void process_sci_queue_work(struct work_struct *work) +{ + process_sci_queue(true); +} + +static DECLARE_WORK(sci_work, process_sci_queue_work); + static irqreturn_t xo1_sci_intr(int irq, void *dev_id) { struct platform_device *pdev = dev_id; @@ -51,6 +106,11 @@ static irqreturn_t xo1_sci_intr(int irq, void *dev_id) input_sync(power_button_idev); } + if (gpe & CS5536_GPIOM7_PME_FLAG) { /* EC GPIO */ + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS); + schedule_work(&sci_work); + } + return IRQ_HANDLED; } @@ -60,6 +120,19 @@ static int xo1_sci_suspend(struct platform_device *pdev, pm_message_t state) olpc_xo1_pm_wakeup_set(CS5536_PM_PWRBTN); else olpc_xo1_pm_wakeup_clear(CS5536_PM_PWRBTN); + + if (device_may_wakeup(&ebook_switch_idev->dev)) + olpc_ec_wakeup_set(EC_SCI_SRC_EBOOK); + else + olpc_ec_wakeup_clear(EC_SCI_SRC_EBOOK); + + return 0; +} + +static int xo1_sci_resume(struct platform_device *pdev) +{ + /* Enable all EC events */ + olpc_ec_mask_write(EC_SCI_SRC_ALL); return 0; } @@ -104,6 +177,50 @@ static int __devinit setup_sci_interrupt(struct platform_device *pdev) return r; } +static int __devinit setup_ec_sci(void) +{ + int r; + + r = gpio_request(OLPC_GPIO_ECSCI, "OLPC-ECSCI"); + if (r) + return r; + + gpio_direction_input(OLPC_GPIO_ECSCI); + + /* Clear pending EC SCI events */ + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_NEGATIVE_EDGE_STS); + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_POSITIVE_EDGE_STS); + + /* + * Enable EC SCI events, and map them to both a PME and the SCI + * interrupt. + * + * Ordinarily, in addition to functioning as GPIOs, Geode GPIOs can + * be mapped to regular interrupts *or* Geode-specific Power + * Management Events (PMEs) - events that bring the system out of + * suspend. In this case, we want both of those things - the system + * wakeup, *and* the ability to get an interrupt when an event occurs. + * + * To achieve this, we map the GPIO to a PME, and then we use one + * of the many generic knobs on the CS5535 PIC to additionally map the + * PME to the regular SCI interrupt line. + */ + cs5535_gpio_set(OLPC_GPIO_ECSCI, GPIO_EVENTS_ENABLE); + + /* Set the SCI to cause a PME event on group 7 */ + cs5535_gpio_setup_event(OLPC_GPIO_ECSCI, 7, 1); + + /* And have group 7 also fire the SCI interrupt */ + cs5535_pic_unreqz_select_high(7, sci_irq); + + return 0; +} + +static void free_ec_sci(void) +{ + gpio_free(OLPC_GPIO_ECSCI); +} + static int __devinit setup_power_button(struct platform_device *pdev) { int r; @@ -135,6 +252,37 @@ static void free_power_button(void) input_free_device(power_button_idev); } +static int __devinit setup_ebook_switch(struct platform_device *pdev) +{ + int r; + + ebook_switch_idev = input_allocate_device(); + if (!ebook_switch_idev) + return -ENOMEM; + + ebook_switch_idev->name = "EBook Switch"; + ebook_switch_idev->phys = DRV_NAME "/input1"; + set_bit(EV_SW, ebook_switch_idev->evbit); + set_bit(SW_TABLET_MODE, ebook_switch_idev->swbit); + + ebook_switch_idev->dev.parent = &pdev->dev; + device_set_wakeup_capable(&ebook_switch_idev->dev, true); + + r = input_register_device(ebook_switch_idev); + if (r) { + dev_err(&pdev->dev, "failed to register ebook switch: %d\n", r); + input_free_device(ebook_switch_idev); + } + + return r; +} + +static void free_ebook_switch(void) +{ + input_unregister_device(ebook_switch_idev); + input_free_device(ebook_switch_idev); +} + static int __devinit xo1_sci_probe(struct platform_device *pdev) { struct resource *res; @@ -159,10 +307,39 @@ static int __devinit xo1_sci_probe(struct platform_device *pdev) if (r) return r; + r = setup_ebook_switch(pdev); + if (r) + goto err_ebook; + + r = setup_ec_sci(); + if (r) + goto err_ecsci; + + /* Enable PME generation for EC-generated events */ + outl(CS5536_GPIOM7_PME_EN, acpi_base + CS5536_PM_GPE0_EN); + + /* Clear pending events */ + outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS); + process_sci_queue(false); + + /* Initial sync */ + send_ebook_state(); + r = setup_sci_interrupt(pdev); if (r) - free_power_button(); + goto err_sci; + /* Enable all EC events */ + olpc_ec_mask_write(EC_SCI_SRC_ALL); + + return r; + +err_sci: + free_ec_sci(); +err_ecsci: + free_ebook_switch(); +err_ebook: + free_power_button(); return r; } @@ -170,6 +347,9 @@ static int __devexit xo1_sci_remove(struct platform_device *pdev) { mfd_cell_disable(pdev); free_irq(sci_irq, pdev); + cancel_work_sync(&sci_work); + free_ec_sci(); + free_ebook_switch(); free_power_button(); acpi_base = 0; return 0; @@ -182,6 +362,7 @@ static struct platform_driver xo1_sci_driver = { .probe = xo1_sci_probe, .remove = __devexit_p(xo1_sci_remove), .suspend = xo1_sci_suspend, + .resume = xo1_sci_resume, }; static int __init xo1_sci_init(void) diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h index 6f78235cb905..d7e9a7f6ddb0 100644 --- a/include/linux/cs5535.h +++ b/include/linux/cs5535.h @@ -11,6 +11,8 @@ #ifndef _CS5535_H #define _CS5535_H +#include + /* MSRs */ #define MSR_GLIU_P2D_RO0 0x10000029 @@ -43,6 +45,18 @@ #define MSR_GX_GLD_MSR_CONFIG 0xC0002001 #define MSR_GX_MSR_PADSEL 0xC0002011 +static inline int cs5535_pic_unreqz_select_high(unsigned int group, + unsigned int irq) +{ + uint32_t lo, hi; + + rdmsr(MSR_PIC_ZSEL_HIGH, lo, hi); + lo &= ~(0xF << (group * 4)); + lo |= (irq & 0xF) << (group * 4); + wrmsr(MSR_PIC_ZSEL_HIGH, lo, hi); + return 0; +} + /* PIC registers */ #define CS5536_PIC_INT_SEL1 0x4d0 #define CS5536_PIC_INT_SEL2 0x4d1 @@ -73,6 +87,7 @@ #define CS5536_PM1_EN 0x02 #define CS5536_PM1_CNT 0x08 #define CS5536_PM_GPE0_STS 0x18 +#define CS5536_PM_GPE0_EN 0x1c /* CS5536_PM1_STS bits */ #define CS5536_WAK_FLAG (1 << 15) @@ -81,6 +96,13 @@ /* CS5536_PM1_EN bits */ #define CS5536_PM_PWRBTN (1 << 8) +/* CS5536_PM_GPE0_STS bits */ +#define CS5536_GPIOM7_PME_FLAG (1 << 31) +#define CS5536_GPIOM6_PME_FLAG (1 << 30) + +/* CS5536_PM_GPE0_EN bits */ +#define CS5536_GPIOM7_PME_EN (1 << 31) + /* VSA2 magic values */ #define VSA_VRC_INDEX 0xAC1C #define VSA_VRC_DATA 0xAC1E -- cgit v1.2.3 From 2cf2baea103f0a3d68b0f989d28df66f16dbc834 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sat, 25 Jun 2011 17:34:15 +0100 Subject: x86, olpc-xo1-sci: Add lid switch functionality Configure the XO-1's lid switch GPIO to trigger an SCI interrupt, and correctly expose this input device which can be used as a wakeup source. Signed-off-by: Daniel Drake Link: http://lkml.kernel.org/r/1309019658-1712-9-git-send-email-dsd@laptop.org Acked-by: Andres Salomon Signed-off-by: H. Peter Anvin --- arch/x86/Kconfig | 1 + arch/x86/platform/olpc/olpc-xo1-sci.c | 207 +++++++++++++++++++++++++++++++++- include/linux/cs5535.h | 1 + 3 files changed, 208 insertions(+), 1 deletion(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 88889106ac9b..350ccbb4d650 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2090,6 +2090,7 @@ config OLPC_XO1_SCI - EC-driven system wakeups - Power button - Ebook switch + - Lid switch endif # X86_32 diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c index 63f50506078b..ad0670bca833 100644 --- a/arch/x86/platform/olpc/olpc-xo1-sci.c +++ b/arch/x86/platform/olpc/olpc-xo1-sci.c @@ -32,9 +32,26 @@ static unsigned long acpi_base; static struct input_dev *power_button_idev; static struct input_dev *ebook_switch_idev; +static struct input_dev *lid_switch_idev; static int sci_irq; +static bool lid_open; +static bool lid_inverted; +static int lid_wake_mode; + +enum lid_wake_modes { + LID_WAKE_ALWAYS, + LID_WAKE_OPEN, + LID_WAKE_CLOSE, +}; + +static const char * const lid_wake_mode_names[] = { + [LID_WAKE_ALWAYS] = "always", + [LID_WAKE_OPEN] = "open", + [LID_WAKE_CLOSE] = "close", +}; + /* Report current ebook switch state through input layer */ static void send_ebook_state(void) { @@ -49,6 +66,70 @@ static void send_ebook_state(void) input_sync(ebook_switch_idev); } +static void flip_lid_inverter(void) +{ + /* gpio is high; invert so we'll get l->h event interrupt */ + if (lid_inverted) + cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_INPUT_INVERT); + else + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_INPUT_INVERT); + lid_inverted = !lid_inverted; +} + +static void detect_lid_state(void) +{ + /* + * the edge detector hookup on the gpio inputs on the geode is + * odd, to say the least. See http://dev.laptop.org/ticket/5703 + * for details, but in a nutshell: we don't use the edge + * detectors. instead, we make use of an anomoly: with the both + * edge detectors turned off, we still get an edge event on a + * positive edge transition. to take advantage of this, we use the + * front-end inverter to ensure that that's the edge we're always + * going to see next. + */ + + int state; + + state = cs5535_gpio_isset(OLPC_GPIO_LID, GPIO_READ_BACK); + lid_open = !state ^ !lid_inverted; /* x ^^ y */ + if (!state) + return; + + flip_lid_inverter(); +} + +/* Report current lid switch state through input layer */ +static void send_lid_state(void) +{ + input_report_switch(lid_switch_idev, SW_LID, !lid_open); + input_sync(lid_switch_idev); +} + +static ssize_t lid_wake_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const char *mode = lid_wake_mode_names[lid_wake_mode]; + return sprintf(buf, "%s\n", mode); +} +static ssize_t lid_wake_mode_set(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int i; + for (i = 0; i < ARRAY_SIZE(lid_wake_mode_names); i++) { + const char *mode = lid_wake_mode_names[i]; + if (strlen(mode) != count || strncasecmp(mode, buf, count)) + continue; + + lid_wake_mode = i; + return count; + } + return -EINVAL; +} +static DEVICE_ATTR(lid_wake_mode, S_IWUSR | S_IRUGO, lid_wake_mode_show, + lid_wake_mode_set); + /* * Process all items in the EC's SCI queue. * @@ -111,6 +192,11 @@ static irqreturn_t xo1_sci_intr(int irq, void *dev_id) schedule_work(&sci_work); } + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS); + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS); + detect_lid_state(); + send_lid_state(); + return IRQ_HANDLED; } @@ -126,11 +212,32 @@ static int xo1_sci_suspend(struct platform_device *pdev, pm_message_t state) else olpc_ec_wakeup_clear(EC_SCI_SRC_EBOOK); + if (!device_may_wakeup(&lid_switch_idev->dev)) { + cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE); + } else if ((lid_open && lid_wake_mode == LID_WAKE_OPEN) || + (!lid_open && lid_wake_mode == LID_WAKE_CLOSE)) { + flip_lid_inverter(); + + /* we may have just caused an event */ + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS); + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS); + + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE); + } + return 0; } static int xo1_sci_resume(struct platform_device *pdev) { + /* + * We don't know what may have happened while we were asleep. + * Reestablish our lid setup so we're sure to catch all transitions. + */ + detect_lid_state(); + send_lid_state(); + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE); + /* Enable all EC events */ olpc_ec_mask_write(EC_SCI_SRC_ALL); return 0; @@ -221,6 +328,43 @@ static void free_ec_sci(void) gpio_free(OLPC_GPIO_ECSCI); } +static int __devinit setup_lid_events(void) +{ + int r; + + r = gpio_request(OLPC_GPIO_LID, "OLPC-LID"); + if (r) + return r; + + gpio_direction_input(OLPC_GPIO_LID); + + cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_INPUT_INVERT); + lid_inverted = 0; + + /* Clear edge detection and event enable for now */ + cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE); + cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_EN); + cs5535_gpio_clear(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_EN); + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_NEGATIVE_EDGE_STS); + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_POSITIVE_EDGE_STS); + + /* Set the LID to cause an PME event on group 6 */ + cs5535_gpio_setup_event(OLPC_GPIO_LID, 6, 1); + + /* Set PME group 6 to fire the SCI interrupt */ + cs5535_gpio_set_irq(6, sci_irq); + + /* Enable the event */ + cs5535_gpio_set(OLPC_GPIO_LID, GPIO_EVENTS_ENABLE); + + return 0; +} + +static void free_lid_events(void) +{ + gpio_free(OLPC_GPIO_LID); +} + static int __devinit setup_power_button(struct platform_device *pdev) { int r; @@ -283,6 +427,50 @@ static void free_ebook_switch(void) input_free_device(ebook_switch_idev); } +static int __devinit setup_lid_switch(struct platform_device *pdev) +{ + int r; + + lid_switch_idev = input_allocate_device(); + if (!lid_switch_idev) + return -ENOMEM; + + lid_switch_idev->name = "Lid Switch"; + lid_switch_idev->phys = DRV_NAME "/input2"; + set_bit(EV_SW, lid_switch_idev->evbit); + set_bit(SW_LID, lid_switch_idev->swbit); + + lid_switch_idev->dev.parent = &pdev->dev; + device_set_wakeup_capable(&lid_switch_idev->dev, true); + + r = input_register_device(lid_switch_idev); + if (r) { + dev_err(&pdev->dev, "failed to register lid switch: %d\n", r); + goto err_register; + } + + r = device_create_file(&lid_switch_idev->dev, &dev_attr_lid_wake_mode); + if (r) { + dev_err(&pdev->dev, "failed to create wake mode attr: %d\n", r); + goto err_create_attr; + } + + return 0; + +err_create_attr: + input_unregister_device(lid_switch_idev); +err_register: + input_free_device(lid_switch_idev); + return r; +} + +static void free_lid_switch(void) +{ + device_remove_file(&lid_switch_idev->dev, &dev_attr_lid_wake_mode); + input_unregister_device(lid_switch_idev); + input_free_device(lid_switch_idev); +} + static int __devinit xo1_sci_probe(struct platform_device *pdev) { struct resource *res; @@ -311,12 +499,21 @@ static int __devinit xo1_sci_probe(struct platform_device *pdev) if (r) goto err_ebook; + r = setup_lid_switch(pdev); + if (r) + goto err_lid; + + r = setup_lid_events(); + if (r) + goto err_lidevt; + r = setup_ec_sci(); if (r) goto err_ecsci; /* Enable PME generation for EC-generated events */ - outl(CS5536_GPIOM7_PME_EN, acpi_base + CS5536_PM_GPE0_EN); + outl(CS5536_GPIOM6_PME_EN | CS5536_GPIOM7_PME_EN, + acpi_base + CS5536_PM_GPE0_EN); /* Clear pending events */ outl(0xffffffff, acpi_base + CS5536_PM_GPE0_STS); @@ -324,6 +521,8 @@ static int __devinit xo1_sci_probe(struct platform_device *pdev) /* Initial sync */ send_ebook_state(); + detect_lid_state(); + send_lid_state(); r = setup_sci_interrupt(pdev); if (r) @@ -337,6 +536,10 @@ static int __devinit xo1_sci_probe(struct platform_device *pdev) err_sci: free_ec_sci(); err_ecsci: + free_lid_events(); +err_lidevt: + free_lid_switch(); +err_lid: free_ebook_switch(); err_ebook: free_power_button(); @@ -349,6 +552,8 @@ static int __devexit xo1_sci_remove(struct platform_device *pdev) free_irq(sci_irq, pdev); cancel_work_sync(&sci_work); free_ec_sci(); + free_lid_events(); + free_lid_switch(); free_ebook_switch(); free_power_button(); acpi_base = 0; diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h index d7e9a7f6ddb0..72954c6bf5d6 100644 --- a/include/linux/cs5535.h +++ b/include/linux/cs5535.h @@ -102,6 +102,7 @@ static inline int cs5535_pic_unreqz_select_high(unsigned int group, /* CS5536_PM_GPE0_EN bits */ #define CS5536_GPIOM7_PME_EN (1 << 31) +#define CS5536_GPIOM6_PME_EN (1 << 30) /* VSA2 magic values */ #define VSA_VRC_INDEX 0xAC1C -- cgit v1.2.3 From e1040ac693bac19eaeafbd6c5fd24d9429b5eeb8 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sat, 25 Jun 2011 17:34:16 +0100 Subject: x86, olpc-xo1-sci: Propagate power supply/battery events EC events indicate change in AC power connectivity, battery state of charge, battery error, battery presence, etc. Send notifications to the power supply subsystem when changes are detected. Signed-off-by: Daniel Drake Link: http://lkml.kernel.org/r/1309019658-1712-10-git-send-email-dsd@laptop.org Acked-by: Andres Salomon Signed-off-by: H. Peter Anvin --- arch/x86/Kconfig | 4 +++- arch/x86/platform/olpc/olpc-xo1-sci.c | 37 +++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 350ccbb4d650..a6aefbbc5cbf 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2082,7 +2082,7 @@ config OLPC_XO1_PM config OLPC_XO1_SCI bool "OLPC XO-1 SCI extras" - depends on OLPC && OLPC_XO1_PM + depends on OLPC && OLPC_XO1_PM && POWER_SUPPLY select GPIO_CS5535 select MFD_CORE ---help--- @@ -2091,6 +2091,8 @@ config OLPC_XO1_SCI - Power button - Ebook switch - Lid switch + - AC adapter status updates + - Battery status updates endif # X86_32 diff --git a/arch/x86/platform/olpc/olpc-xo1-sci.c b/arch/x86/platform/olpc/olpc-xo1-sci.c index ad0670bca833..1d4c783d7325 100644 --- a/arch/x86/platform/olpc/olpc-xo1-sci.c +++ b/arch/x86/platform/olpc/olpc-xo1-sci.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -52,6 +53,26 @@ static const char * const lid_wake_mode_names[] = { [LID_WAKE_CLOSE] = "close", }; +static void battery_status_changed(void) +{ + struct power_supply *psy = power_supply_get_by_name("olpc-battery"); + + if (psy) { + power_supply_changed(psy); + put_device(psy->dev); + } +} + +static void ac_status_changed(void) +{ + struct power_supply *psy = power_supply_get_by_name("olpc-ac"); + + if (psy) { + power_supply_changed(psy); + put_device(psy->dev); + } +} + /* Report current ebook switch state through input layer */ static void send_ebook_state(void) { @@ -151,6 +172,18 @@ static void process_sci_queue(bool propagate_events) pr_debug(PFX "SCI 0x%x received\n", data); + switch (data) { + case EC_SCI_SRC_BATERR: + case EC_SCI_SRC_BATSOC: + case EC_SCI_SRC_BATTERY: + case EC_SCI_SRC_BATCRIT: + battery_status_changed(); + break; + case EC_SCI_SRC_ACPWR: + ac_status_changed(); + break; + } + if (data == EC_SCI_SRC_EBOOK && propagate_events) send_ebook_state(); } while (data); @@ -240,6 +273,10 @@ static int xo1_sci_resume(struct platform_device *pdev) /* Enable all EC events */ olpc_ec_mask_write(EC_SCI_SRC_ALL); + + /* Power/battery status might have changed too */ + battery_status_changed(); + ac_status_changed(); return 0; } -- cgit v1.2.3 From cfee95977bea090ae5ec4fd442ebd381792d46c4 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sat, 25 Jun 2011 17:34:17 +0100 Subject: x86, olpc: Add XO-1 RTC driver Add a driver to configure the XO-1 RTC via CS5536 MSRs, to be used as a system wakeup source via olpc-xo1-pm. Device detection is based on finding the relevant device tree node. Signed-off-by: Daniel Drake Link: http://lkml.kernel.org/r/1309019658-1712-11-git-send-email-dsd@laptop.org Acked-by: Andres Salomon Acked-by: Grant Likely Reviewed-by: Sebastian Andrzej Siewior Cc: devicetree-discuss@lists.ozlabs.org Signed-off-by: H. Peter Anvin --- .../devicetree/bindings/rtc/olpc-xo1-rtc.txt | 5 ++ arch/x86/Kconfig | 7 ++ arch/x86/platform/olpc/Makefile | 1 + arch/x86/platform/olpc/olpc-xo1-rtc.c | 81 ++++++++++++++++++++++ include/linux/cs5535.h | 5 ++ 5 files changed, 99 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt create mode 100644 arch/x86/platform/olpc/olpc-xo1-rtc.c (limited to 'arch/x86/platform') diff --git a/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt b/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt new file mode 100644 index 000000000000..a2891ceb6344 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/olpc-xo1-rtc.txt @@ -0,0 +1,5 @@ +OLPC XO-1 RTC +~~~~~~~~~~~~~ + +Required properties: + - compatible : "olpc,xo1-rtc" diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index a6aefbbc5cbf..0a9d573a2f9d 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2080,6 +2080,13 @@ config OLPC_XO1_PM ---help--- Add support for poweroff and suspend of the OLPC XO-1 laptop. +config OLPC_XO1_RTC + bool "OLPC XO-1 Real Time Clock" + depends on OLPC_XO1_PM && RTC_DRV_CMOS + ---help--- + Add support for the XO-1 real time clock, which can be used as a + programmable wakeup source. + config OLPC_XO1_SCI bool "OLPC XO-1 SCI extras" depends on OLPC && OLPC_XO1_PM && POWER_SUPPLY diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile index 1ec5ade97f44..8922b9b3cd7a 100644 --- a/arch/x86/platform/olpc/Makefile +++ b/arch/x86/platform/olpc/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o xo1-wakeup.o +obj-$(CONFIG_OLPC_XO1_RTC) += olpc-xo1-rtc.o obj-$(CONFIG_OLPC_XO1_SCI) += olpc-xo1-sci.o diff --git a/arch/x86/platform/olpc/olpc-xo1-rtc.c b/arch/x86/platform/olpc/olpc-xo1-rtc.c new file mode 100644 index 000000000000..a2b4efddd61a --- /dev/null +++ b/arch/x86/platform/olpc/olpc-xo1-rtc.c @@ -0,0 +1,81 @@ +/* + * Support for OLPC XO-1 Real Time Clock (RTC) + * + * Copyright (C) 2011 One Laptop per Child + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include + +static void rtc_wake_on(struct device *dev) +{ + olpc_xo1_pm_wakeup_set(CS5536_PM_RTC); +} + +static void rtc_wake_off(struct device *dev) +{ + olpc_xo1_pm_wakeup_clear(CS5536_PM_RTC); +} + +static struct resource rtc_platform_resource[] = { + [0] = { + .start = RTC_PORT(0), + .end = RTC_PORT(1), + .flags = IORESOURCE_IO, + }, + [1] = { + .start = RTC_IRQ, + .end = RTC_IRQ, + .flags = IORESOURCE_IRQ, + } +}; + +static struct cmos_rtc_board_info rtc_info = { + .rtc_day_alarm = 0, + .rtc_mon_alarm = 0, + .rtc_century = 0, + .wake_on = rtc_wake_on, + .wake_off = rtc_wake_off, +}; + +static struct platform_device xo1_rtc_device = { + .name = "rtc_cmos", + .id = -1, + .num_resources = ARRAY_SIZE(rtc_platform_resource), + .dev.platform_data = &rtc_info, + .resource = rtc_platform_resource, +}; + +static int __init xo1_rtc_init(void) +{ + int r; + struct device_node *node; + + node = of_find_compatible_node(NULL, NULL, "olpc,xo1-rtc"); + if (!node) + return 0; + of_node_put(node); + + pr_info("olpc-xo1-rtc: Initializing OLPC XO-1 RTC\n"); + rdmsrl(MSR_RTC_DOMA_OFFSET, rtc_info.rtc_day_alarm); + rdmsrl(MSR_RTC_MONA_OFFSET, rtc_info.rtc_mon_alarm); + rdmsrl(MSR_RTC_CEN_OFFSET, rtc_info.rtc_century); + + r = platform_device_register(&xo1_rtc_device); + if (r) + return r; + + device_init_wakeup(&xo1_rtc_device.dev, 1); + return 0; +} +arch_initcall(xo1_rtc_init); diff --git a/include/linux/cs5535.h b/include/linux/cs5535.h index 72954c6bf5d6..c077aec3a6ff 100644 --- a/include/linux/cs5535.h +++ b/include/linux/cs5535.h @@ -40,6 +40,10 @@ #define MSR_MFGPT_NR 0x51400029 #define MSR_MFGPT_SETUP 0x5140002B +#define MSR_RTC_DOMA_OFFSET 0x51400055 +#define MSR_RTC_MONA_OFFSET 0x51400056 +#define MSR_RTC_CEN_OFFSET 0x51400057 + #define MSR_LX_SPARE_MSR 0x80000011 /* DC-specific */ #define MSR_GX_GLD_MSR_CONFIG 0xC0002001 @@ -95,6 +99,7 @@ static inline int cs5535_pic_unreqz_select_high(unsigned int group, /* CS5536_PM1_EN bits */ #define CS5536_PM_PWRBTN (1 << 8) +#define CS5536_PM_RTC (1 << 10) /* CS5536_PM_GPE0_STS bits */ #define CS5536_GPIOM7_PME_FLAG (1 << 31) -- cgit v1.2.3 From a0f30f592d2d81e28f3ed7fea7f03246b0d55b75 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sat, 25 Jun 2011 17:34:18 +0100 Subject: x86, olpc: Add XO-1.5 SCI driver Add a driver for the ACPI-based EC event interface found on the OLPC XO-1.5 laptop. This enables notification of battery/AC power events, and enables various devices to be used as wakeup sources through regular ACPI mechanisms. This driver can't be built as a module, because some drivers need to know at boot-time if SCI-based functionality is available via olpc_ec_wakeup_available(). Signed-off-by: Daniel Drake Link: http://lkml.kernel.org/r/1309019658-1712-12-git-send-email-dsd@laptop.org Acked-by: Andres Salomon Signed-off-by: H. Peter Anvin --- arch/x86/Kconfig | 9 ++ arch/x86/platform/olpc/Makefile | 1 + arch/x86/platform/olpc/olpc-xo15-sci.c | 168 +++++++++++++++++++++++++++++++++ arch/x86/platform/olpc/olpc.c | 9 ++ 4 files changed, 187 insertions(+) create mode 100644 arch/x86/platform/olpc/olpc-xo15-sci.c (limited to 'arch/x86/platform') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 0a9d573a2f9d..8af5ba8a36b7 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2101,6 +2101,15 @@ config OLPC_XO1_SCI - AC adapter status updates - Battery status updates +config OLPC_XO15_SCI + bool "OLPC XO-1.5 SCI extras" + depends on OLPC && ACPI && POWER_SUPPLY + ---help--- + Add support for SCI-based features of the OLPC XO-1.5 laptop: + - EC-driven system wakeups + - AC adapter status updates + - Battery status updates + endif # X86_32 config AMD_NB diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile index 8922b9b3cd7a..fd332c533947 100644 --- a/arch/x86/platform/olpc/Makefile +++ b/arch/x86/platform/olpc/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_OLPC) += olpc.o olpc_ofw.o olpc_dt.o obj-$(CONFIG_OLPC_XO1_PM) += olpc-xo1-pm.o xo1-wakeup.o obj-$(CONFIG_OLPC_XO1_RTC) += olpc-xo1-rtc.o obj-$(CONFIG_OLPC_XO1_SCI) += olpc-xo1-sci.o +obj-$(CONFIG_OLPC_XO15_SCI) += olpc-xo15-sci.o diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c new file mode 100644 index 000000000000..a5990ebadbe4 --- /dev/null +++ b/arch/x86/platform/olpc/olpc-xo15-sci.c @@ -0,0 +1,168 @@ +/* + * Support for OLPC XO-1.5 System Control Interrupts (SCI) + * + * Copyright (C) 2009-2010 One Laptop per Child + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#define DRV_NAME "olpc-xo15-sci" +#define PFX DRV_NAME ": " +#define XO15_SCI_CLASS DRV_NAME +#define XO15_SCI_DEVICE_NAME "OLPC XO-1.5 SCI" + +static unsigned long xo15_sci_gpe; + +static void battery_status_changed(void) +{ + struct power_supply *psy = power_supply_get_by_name("olpc-battery"); + + if (psy) { + power_supply_changed(psy); + put_device(psy->dev); + } +} + +static void ac_status_changed(void) +{ + struct power_supply *psy = power_supply_get_by_name("olpc-ac"); + + if (psy) { + power_supply_changed(psy); + put_device(psy->dev); + } +} + +static void process_sci_queue(void) +{ + u16 data; + int r; + + do { + r = olpc_ec_sci_query(&data); + if (r || !data) + break; + + pr_debug(PFX "SCI 0x%x received\n", data); + + switch (data) { + case EC_SCI_SRC_BATERR: + case EC_SCI_SRC_BATSOC: + case EC_SCI_SRC_BATTERY: + case EC_SCI_SRC_BATCRIT: + battery_status_changed(); + break; + case EC_SCI_SRC_ACPWR: + ac_status_changed(); + break; + } + } while (data); + + if (r) + pr_err(PFX "Failed to clear SCI queue"); +} + +static void process_sci_queue_work(struct work_struct *work) +{ + process_sci_queue(); +} + +static DECLARE_WORK(sci_work, process_sci_queue_work); + +static u32 xo15_sci_gpe_handler(acpi_handle gpe_device, u32 gpe, void *context) +{ + schedule_work(&sci_work); + return ACPI_INTERRUPT_HANDLED | ACPI_REENABLE_GPE; +} + +static int xo15_sci_add(struct acpi_device *device) +{ + unsigned long long tmp; + acpi_status status; + + if (!device) + return -EINVAL; + + strcpy(acpi_device_name(device), XO15_SCI_DEVICE_NAME); + strcpy(acpi_device_class(device), XO15_SCI_CLASS); + + /* Get GPE bit assignment (EC events). */ + status = acpi_evaluate_integer(device->handle, "_GPE", NULL, &tmp); + if (ACPI_FAILURE(status)) + return -EINVAL; + + xo15_sci_gpe = tmp; + status = acpi_install_gpe_handler(NULL, xo15_sci_gpe, + ACPI_GPE_EDGE_TRIGGERED, + xo15_sci_gpe_handler, device); + if (ACPI_FAILURE(status)) + return -ENODEV; + + dev_info(&device->dev, "Initialized, GPE = 0x%lx\n", xo15_sci_gpe); + + /* Flush queue, and enable all SCI events */ + process_sci_queue(); + olpc_ec_mask_write(EC_SCI_SRC_ALL); + + acpi_enable_gpe(NULL, xo15_sci_gpe); + + /* Enable wake-on-EC */ + if (device->wakeup.flags.valid) + device_set_wakeup_enable(&device->dev, true); + + return 0; +} + +static int xo15_sci_remove(struct acpi_device *device, int type) +{ + acpi_disable_gpe(NULL, xo15_sci_gpe); + acpi_remove_gpe_handler(NULL, xo15_sci_gpe, xo15_sci_gpe_handler); + cancel_work_sync(&sci_work); + return 0; +} + +static int xo15_sci_resume(struct acpi_device *device) +{ + /* Enable all EC events */ + olpc_ec_mask_write(EC_SCI_SRC_ALL); + + /* Power/battery status might have changed */ + battery_status_changed(); + ac_status_changed(); + + return 0; +} + +static const struct acpi_device_id xo15_sci_device_ids[] = { + {"XO15EC", 0}, + {"", 0}, +}; + +static struct acpi_driver xo15_sci_drv = { + .name = DRV_NAME, + .class = XO15_SCI_CLASS, + .ids = xo15_sci_device_ids, + .ops = { + .add = xo15_sci_add, + .remove = xo15_sci_remove, + .resume = xo15_sci_resume, + }, +}; + +static int __init xo15_sci_init(void) +{ + return acpi_bus_register_driver(&xo15_sci_drv); +} +device_initcall(xo15_sci_init); diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c index 72fd041762fc..8b9940e78e2f 100644 --- a/arch/x86/platform/olpc/olpc.c +++ b/arch/x86/platform/olpc/olpc.c @@ -222,6 +222,15 @@ bool olpc_ec_wakeup_available(void) return true; #endif + /* + * XO-1.5 EC wakeups are available when olpc-xo15-sci driver is + * compiled in + */ +#ifdef CONFIG_OLPC_XO15_SCI + if (olpc_platform_info.boardrev >= olpc_board_pre(0xd0)) /* XO-1.5 */ + return true; +#endif + return false; } EXPORT_SYMBOL_GPL(olpc_ec_wakeup_available); -- cgit v1.2.3 From ef68c8f87ed13f65df867dddf36c0e185b27b942 Mon Sep 17 00:00:00 2001 From: Jan Beulich Date: Tue, 19 Jul 2011 11:53:07 +0100 Subject: x86: Serialize EFI time accesses on rtc_lock The EFI specification requires that callers of the time related runtime functions serialize with other CMOS accesses in the kernel, as the EFI time functions may choose to also use the legacy CMOS RTC. Besides fixing a latent bug, this is a prerequisite to safely enable the rtc-efi driver for x86, which ought to be preferred over rtc-cmos on all EFI platforms. Signed-off-by: Jan Beulich Acked-by: Matthew Garrett Cc: Link: http://lkml.kernel.org/r/4E257E33020000780004E319@nat28.tlf.novell.com Signed-off-by: Ingo Molnar Cc: Matthew Garrett --- arch/x86/platform/efi/efi.c | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 474356b98ede..b8823d5a0117 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -79,26 +79,50 @@ early_param("add_efi_memmap", setup_add_efi_memmap); static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) { - return efi_call_virt2(get_time, tm, tc); + unsigned long flags; + efi_status_t status; + + spin_lock_irqsave(&rtc_lock, flags); + status = efi_call_virt2(get_time, tm, tc); + spin_unlock_irqrestore(&rtc_lock, flags); + return status; } static efi_status_t virt_efi_set_time(efi_time_t *tm) { - return efi_call_virt1(set_time, tm); + unsigned long flags; + efi_status_t status; + + spin_lock_irqsave(&rtc_lock, flags); + status = efi_call_virt1(set_time, tm); + spin_unlock_irqrestore(&rtc_lock, flags); + return status; } static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, efi_bool_t *pending, efi_time_t *tm) { - return efi_call_virt3(get_wakeup_time, - enabled, pending, tm); + unsigned long flags; + efi_status_t status; + + spin_lock_irqsave(&rtc_lock, flags); + status = efi_call_virt3(get_wakeup_time, + enabled, pending, tm); + spin_unlock_irqrestore(&rtc_lock, flags); + return status; } static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) { - return efi_call_virt2(set_wakeup_time, - enabled, tm); + unsigned long flags; + efi_status_t status; + + spin_lock_irqsave(&rtc_lock, flags); + status = efi_call_virt2(set_wakeup_time, + enabled, tm); + spin_unlock_irqrestore(&rtc_lock, flags); + return status; } static efi_status_t virt_efi_get_variable(efi_char16_t *name, @@ -164,11 +188,14 @@ static efi_status_t __init phys_efi_set_virtual_address_map( static efi_status_t __init phys_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) { + unsigned long flags; efi_status_t status; + spin_lock_irqsave(&rtc_lock, flags); efi_call_phys_prelog(); status = efi_call_phys2(efi_phys.get_time, tm, tc); efi_call_phys_epilog(); + spin_unlock_irqrestore(&rtc_lock, flags); return status; } -- cgit v1.2.3 From 07d5b38e14b7ff98eb52e4a6db4e20abcc608da3 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sun, 24 Jul 2011 18:34:30 +0100 Subject: x86, olpc-xo15-sci: Enable EC wakeup capability Some recent changes to the way that ACPI handles wakeup flags means that the XO15EC ACPI device is not wakeup-capable by default so device_set_wakeup_enable() does nothing. Use device_init_wakeup() to mark the device as wakeup capable, and to enable wakeups. Signed-off-by: Daniel Drake Link: http://lkml.kernel.org/r/20110724173430.BE03C9D401C@zog.reactivated.net Signed-off-by: Ingo Molnar --- arch/x86/platform/olpc/olpc-xo15-sci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c index a5990ebadbe4..2b235b77d9ab 100644 --- a/arch/x86/platform/olpc/olpc-xo15-sci.c +++ b/arch/x86/platform/olpc/olpc-xo15-sci.c @@ -120,7 +120,7 @@ static int xo15_sci_add(struct acpi_device *device) /* Enable wake-on-EC */ if (device->wakeup.flags.valid) - device_set_wakeup_enable(&device->dev, true); + device_init_wakeup(&device->dev, true); return 0; } -- cgit v1.2.3 From 6dccf9c508d5d773859df1cc2dce75c5b19e35a0 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Tue, 12 Jul 2011 22:29:32 -0400 Subject: mrst_pmu: driver for Intel Moorestown Power Management Unit The Moorestown (MRST) Power Management Unit (PMU) driver directs the SOC power states in the "Langwell" south complex (SCU). It hooks pci_platform_pm_ops[] and thus observes all PCI ".set_state" requests. For devices in the SC, the pmu driver translates those PCI requests into the appropriate commands for the SCU. The PMU driver helps implement S0i3, a deep system idle power idle state. Entry into S0i3 is via cpuidle, just like regular processor c-states. S0i3 depends on pre-conditions including uni-processor, graphics off, and certain IO devices in the SC must be off. If those pre-conditions are met, then the PMU allows cpuidle to enter S0i3, otherwise such requests are demoted, either to Atom C4 or Atom C6. This driver is based on prototype work by Bruce Flemming, Illyas Mansoor, Rajeev D. Muralidhar, Vishwesh M. Rudramuni, Hari Seshadri and Sujith Thomas. The current driver also includes contributions from H. Peter Anvin, Arjan van de Ven, Kristen Accardi, and Yong Wang. Thanks for additional review feedback from Alan Cox and Randy Dunlap. Acked-by: Alan Cox Acked-by: H. Peter Anvin Signed-off-by: Len Brown --- MAINTAINERS | 6 + arch/x86/platform/mrst/Makefile | 1 + arch/x86/platform/mrst/pmu.c | 817 ++++++++++++++++++++++++++++++++++++++++ arch/x86/platform/mrst/pmu.h | 234 ++++++++++++ 4 files changed, 1058 insertions(+) create mode 100644 arch/x86/platform/mrst/pmu.c create mode 100644 arch/x86/platform/mrst/pmu.h (limited to 'arch/x86/platform') diff --git a/MAINTAINERS b/MAINTAINERS index 187282da9213..d37b387b5109 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3344,6 +3344,12 @@ F: drivers/net/ixgb/ F: drivers/net/ixgbe/ F: drivers/net/ixgbevf/ +INTEL MRST PMU DRIVER +M: Len Brown +L: linux-pm@lists.linux-foundation.org +S: Supported +F: arch/x86/platform/mrst/pmu.* + INTEL PRO/WIRELESS 2100 NETWORK CONNECTION SUPPORT L: linux-wireless@vger.kernel.org S: Orphan diff --git a/arch/x86/platform/mrst/Makefile b/arch/x86/platform/mrst/Makefile index f61ccdd49341..1ea38775a6d3 100644 --- a/arch/x86/platform/mrst/Makefile +++ b/arch/x86/platform/mrst/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_X86_MRST) += mrst.o obj-$(CONFIG_X86_MRST) += vrtc.o obj-$(CONFIG_EARLY_PRINTK_MRST) += early_printk_mrst.o +obj-$(CONFIG_X86_MRST) += pmu.o diff --git a/arch/x86/platform/mrst/pmu.c b/arch/x86/platform/mrst/pmu.c new file mode 100644 index 000000000000..9281da7d91bd --- /dev/null +++ b/arch/x86/platform/mrst/pmu.c @@ -0,0 +1,817 @@ +/* + * mrst/pmu.c - driver for MRST Power Management Unit + * + * Copyright (c) 2011, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pmu.h" + +#define IPCMSG_FW_REVISION 0xF4 + +struct mrst_device { + u16 pci_dev_num; /* DEBUG only */ + u16 lss; + u16 latest_request; + unsigned int pci_state_counts[PCI_D3cold + 1]; /* DEBUG only */ +}; + +/* + * comlete list of MRST PCI devices + */ +static struct mrst_device mrst_devs[] = { +/* 0 */ { 0x0800, LSS_SPI0 }, /* Moorestown SPI Ctrl 0 */ +/* 1 */ { 0x0801, LSS_SPI1 }, /* Moorestown SPI Ctrl 1 */ +/* 2 */ { 0x0802, LSS_I2C0 }, /* Moorestown I2C 0 */ +/* 3 */ { 0x0803, LSS_I2C1 }, /* Moorestown I2C 1 */ +/* 4 */ { 0x0804, LSS_I2C2 }, /* Moorestown I2C 2 */ +/* 5 */ { 0x0805, LSS_KBD }, /* Moorestown Keyboard Ctrl */ +/* 6 */ { 0x0806, LSS_USB_HC }, /* Moorestown USB Ctrl */ +/* 7 */ { 0x0807, LSS_SD_HC0 }, /* Moorestown SD Host Ctrl 0 */ +/* 8 */ { 0x0808, LSS_SD_HC1 }, /* Moorestown SD Host Ctrl 1 */ +/* 9 */ { 0x0809, LSS_NAND }, /* Moorestown NAND Ctrl */ +/* 10 */ { 0x080a, LSS_AUDIO }, /* Moorestown Audio Ctrl */ +/* 11 */ { 0x080b, LSS_IMAGING }, /* Moorestown ISP */ +/* 12 */ { 0x080c, LSS_SECURITY }, /* Moorestown Security Controller */ +/* 13 */ { 0x080d, LSS_DISPLAY }, /* Moorestown External Displays */ +/* 14 */ { 0x080e, 0 }, /* Moorestown SCU IPC */ +/* 15 */ { 0x080f, LSS_GPIO }, /* Moorestown GPIO Controller */ +/* 16 */ { 0x0810, 0 }, /* Moorestown Power Management Unit */ +/* 17 */ { 0x0811, LSS_USB_OTG }, /* Moorestown OTG Ctrl */ +/* 18 */ { 0x0812, LSS_SPI2 }, /* Moorestown SPI Ctrl 2 */ +/* 19 */ { 0x0813, 0 }, /* Moorestown SC DMA */ +/* 20 */ { 0x0814, LSS_AUDIO_LPE }, /* Moorestown LPE DMA */ +/* 21 */ { 0x0815, LSS_AUDIO_SSP }, /* Moorestown SSP0 */ + +/* 22 */ { 0x084F, LSS_SD_HC2 }, /* Moorestown SD Host Ctrl 2 */ + +/* 23 */ { 0x4102, 0 }, /* Lincroft */ +/* 24 */ { 0x4110, 0 }, /* Lincroft */ +}; + +/* n.b. We ignore PCI-id 0x815 in LSS9 b/c MeeGo has no driver for it */ +static u16 mrst_lss9_pci_ids[] = {0x080a, 0x0814, 0}; +static u16 mrst_lss10_pci_ids[] = {0x0800, 0x0801, 0x0802, 0x0803, + 0x0804, 0x0805, 0x080f, 0}; + +/* handle concurrent SMP invokations of pmu_pci_set_power_state() */ +static spinlock_t mrst_pmu_power_state_lock; + +static unsigned int wake_counters[MRST_NUM_LSS]; /* DEBUG only */ +static unsigned int pmu_irq_stats[INT_INVALID + 1]; /* DEBUG only */ + +static int graphics_is_off; +static int lss_s0i3_enabled; +static bool mrst_pmu_s0i3_enable; + +/* debug counters */ +static u32 pmu_wait_ready_calls; +static u32 pmu_wait_ready_udelays; +static u32 pmu_wait_ready_udelays_max; +static u32 pmu_wait_done_calls; +static u32 pmu_wait_done_udelays; +static u32 pmu_wait_done_udelays_max; +static u32 pmu_set_power_state_entry; +static u32 pmu_set_power_state_send_cmd; + +static struct mrst_device *pci_id_2_mrst_dev(u16 pci_dev_num) +{ + int index = 0; + + if ((pci_dev_num >= 0x0800) && (pci_dev_num <= 0x815)) + index = pci_dev_num - 0x800; + else if (pci_dev_num == 0x084F) + index = 22; + else if (pci_dev_num == 0x4102) + index = 23; + else if (pci_dev_num == 0x4110) + index = 24; + + if (pci_dev_num != mrst_devs[index].pci_dev_num) { + WARN_ONCE(1, FW_BUG "Unknown PCI device 0x%04X\n", pci_dev_num); + return 0; + } + + return &mrst_devs[index]; +} + +/** + * mrst_pmu_validate_cstates + * @dev: cpuidle_device + * + * Certain states are not appropriate for governor to pick in some cases. + * This function will be called as cpuidle_device's prepare callback and + * thus tells governor to ignore such states when selecting the next state + * to enter. + */ + +#define IDLE_STATE4_IS_C6 4 +#define IDLE_STATE5_IS_S0I3 5 + +int mrst_pmu_invalid_cstates(void) +{ + int cpu = smp_processor_id(); + + /* + * Demote to C4 if the PMU is busy. + * Since LSS changes leave the busy bit clear... + * busy means either the PMU is waiting for an ACK-C6 that + * isn't coming due to an MWAIT that returned immediately; + * or we returned from S0i3 successfully, and the PMU + * is not done sending us interrupts. + */ + if (pmu_read_busy_status()) + return 1 << IDLE_STATE4_IS_C6 | 1 << IDLE_STATE5_IS_S0I3; + + /* + * Disallow S0i3 if: PMU is not initialized, or CPU1 is active, + * or if device LSS is insufficient, or the GPU is active, + * or if it has been explicitly disabled. + */ + if (!pmu_reg || !cpumask_equal(cpu_online_mask, cpumask_of(cpu)) || + !lss_s0i3_enabled || !graphics_is_off || !mrst_pmu_s0i3_enable) + return 1 << IDLE_STATE5_IS_S0I3; + else + return 0; +} + +/* + * pmu_update_wake_counters(): read PM_WKS, update wake_counters[] + * DEBUG only. + */ +static void pmu_update_wake_counters(void) +{ + int lss; + u32 wake_status; + + wake_status = pmu_read_wks(); + + for (lss = 0; lss < MRST_NUM_LSS; ++lss) { + if (wake_status & (1 << lss)) + wake_counters[lss]++; + } +} + +int mrst_pmu_s0i3_entry(void) +{ + int status; + + /* Clear any possible error conditions */ + pmu_write_ics(0x300); + + /* set wake control to current D-states */ + pmu_write_wssc(S0I3_SSS_TARGET); + + status = mrst_s0i3_entry(PM_S0I3_COMMAND, &pmu_reg->pm_cmd); + pmu_update_wake_counters(); + return status; +} + +/* poll for maximum of 5ms for busy bit to clear */ +static int pmu_wait_ready(void) +{ + int udelays; + + pmu_wait_ready_calls++; + + for (udelays = 0; udelays < 500; ++udelays) { + if (udelays > pmu_wait_ready_udelays_max) + pmu_wait_ready_udelays_max = udelays; + + if (pmu_read_busy_status() == 0) + return 0; + + udelay(10); + pmu_wait_ready_udelays++; + } + + /* + * if this fires, observe + * /sys/kernel/debug/mrst_pmu_wait_ready_calls + * /sys/kernel/debug/mrst_pmu_wait_ready_udelays + */ + WARN_ONCE(1, "SCU not ready for 5ms"); + return -EBUSY; +} +/* poll for maximum of 50ms us for busy bit to clear */ +static int pmu_wait_done(void) +{ + int udelays; + + pmu_wait_done_calls++; + + for (udelays = 0; udelays < 500; ++udelays) { + if (udelays > pmu_wait_done_udelays_max) + pmu_wait_done_udelays_max = udelays; + + if (pmu_read_busy_status() == 0) + return 0; + + udelay(100); + pmu_wait_done_udelays++; + } + + /* + * if this fires, observe + * /sys/kernel/debug/mrst_pmu_wait_done_calls + * /sys/kernel/debug/mrst_pmu_wait_done_udelays + */ + WARN_ONCE(1, "SCU not done for 50ms"); + return -EBUSY; +} + +u32 mrst_pmu_msi_is_disabled(void) +{ + return pmu_msi_is_disabled(); +} + +void mrst_pmu_enable_msi(void) +{ + pmu_msi_enable(); +} + +/** + * pmu_irq - pmu driver interrupt handler + * Context: interrupt context + */ +static irqreturn_t pmu_irq(int irq, void *dummy) +{ + union pmu_pm_ics pmu_ics; + + pmu_ics.value = pmu_read_ics(); + + if (!pmu_ics.bits.pending) + return IRQ_NONE; + + switch (pmu_ics.bits.cause) { + case INT_SPURIOUS: + case INT_CMD_DONE: + case INT_CMD_ERR: + case INT_WAKE_RX: + case INT_SS_ERROR: + case INT_S0IX_MISS: + case INT_NO_ACKC6: + pmu_irq_stats[pmu_ics.bits.cause]++; + break; + default: + pmu_irq_stats[INT_INVALID]++; + } + + pmu_write_ics(pmu_ics.value); /* Clear pending interrupt */ + + return IRQ_HANDLED; +} + +/* + * Translate PCI power management to MRST LSS D-states + */ +static int pci_2_mrst_state(int lss, pci_power_t pci_state) +{ + switch (pci_state) { + case PCI_D0: + if (SSMSK(D0i1, lss) & D0I1_ACG_SSS_TARGET) + return D0i1; + else + return D0; + case PCI_D1: + return D0i1; + case PCI_D2: + return D0i2; + case PCI_D3hot: + case PCI_D3cold: + return D0i3; + default: + WARN(1, "pci_state %d\n", pci_state); + return 0; + } +} + +static int pmu_issue_command(u32 pm_ssc) +{ + union pmu_pm_set_cfg_cmd_t command; + + if (pmu_read_busy_status()) { + pr_debug("pmu is busy, Operation not permitted\n"); + return -1; + } + + /* + * enable interrupts in PMU so that interrupts are + * propagated when ioc bit for a particular set + * command is set + */ + + pmu_irq_enable(); + + /* Configure the sub systems for pmu2 */ + + pmu_write_ssc(pm_ssc); + + /* + * Send the set config command for pmu its configured + * for mode CM_IMMEDIATE & hence with No Trigger + */ + + command.pmu2_params.d_param.cfg_mode = CM_IMMEDIATE; + command.pmu2_params.d_param.cfg_delay = 0; + command.pmu2_params.d_param.rsvd = 0; + + /* construct the command to send SET_CFG to particular PMU */ + command.pmu2_params.d_param.cmd = SET_CFG_CMD; + command.pmu2_params.d_param.ioc = 0; + command.pmu2_params.d_param.mode_id = 0; + command.pmu2_params.d_param.sys_state = SYS_STATE_S0I0; + + /* write the value of PM_CMD into particular PMU */ + pr_debug("pmu command being written %x\n", + command.pmu_pm_set_cfg_cmd_value); + + pmu_write_cmd(command.pmu_pm_set_cfg_cmd_value); + + return 0; +} + +static u16 pmu_min_lss_pci_req(u16 *ids, u16 pci_state) +{ + u16 existing_request; + int i; + + for (i = 0; ids[i]; ++i) { + struct mrst_device *mrst_dev; + + mrst_dev = pci_id_2_mrst_dev(ids[i]); + if (unlikely(!mrst_dev)) + continue; + + existing_request = mrst_dev->latest_request; + if (existing_request < pci_state) + pci_state = existing_request; + } + return pci_state; +} + +/** + * pmu_pci_set_power_state - Callback function is used by all the PCI devices + * for a platform specific device power on/shutdown. + */ + +int pmu_pci_set_power_state(struct pci_dev *pdev, pci_power_t pci_state) +{ + u32 old_sss, new_sss; + int status = 0; + struct mrst_device *mrst_dev; + + pmu_set_power_state_entry++; + + BUG_ON(pdev->vendor != PCI_VENDOR_ID_INTEL); + BUG_ON(pci_state < PCI_D0 || pci_state > PCI_D3cold); + + mrst_dev = pci_id_2_mrst_dev(pdev->device); + if (unlikely(!mrst_dev)) + return -ENODEV; + + mrst_dev->pci_state_counts[pci_state]++; /* count invocations */ + + /* PMU driver calls self as part of PCI initialization, ignore */ + if (pdev->device == PCI_DEV_ID_MRST_PMU) + return 0; + + BUG_ON(!pmu_reg); /* SW bug if called before initialized */ + + spin_lock(&mrst_pmu_power_state_lock); + + if (pdev->d3_delay) { + dev_dbg(&pdev->dev, "d3_delay %d, should be 0\n", + pdev->d3_delay); + pdev->d3_delay = 0; + } + /* + * If Lincroft graphics, simply remember state + */ + if ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY + && !((pdev->class & PCI_SUB_CLASS_MASK) >> 8)) { + if (pci_state == PCI_D0) + graphics_is_off = 0; + else + graphics_is_off = 1; + goto ret; + } + + if (!mrst_dev->lss) + goto ret; /* device with no LSS */ + + if (mrst_dev->latest_request == pci_state) + goto ret; /* no change */ + + mrst_dev->latest_request = pci_state; /* record latest request */ + + /* + * LSS9 and LSS10 contain multiple PCI devices. + * Use the lowest numbered (highest power) state in the LSS + */ + if (mrst_dev->lss == 9) + pci_state = pmu_min_lss_pci_req(mrst_lss9_pci_ids, pci_state); + else if (mrst_dev->lss == 10) + pci_state = pmu_min_lss_pci_req(mrst_lss10_pci_ids, pci_state); + + status = pmu_wait_ready(); + if (status) + goto ret; + + old_sss = pmu_read_sss(); + new_sss = old_sss & ~SSMSK(3, mrst_dev->lss); + new_sss |= SSMSK(pci_2_mrst_state(mrst_dev->lss, pci_state), + mrst_dev->lss); + + if (new_sss == old_sss) + goto ret; /* nothing to do */ + + pmu_set_power_state_send_cmd++; + + status = pmu_issue_command(new_sss); + + if (unlikely(status != 0)) { + dev_err(&pdev->dev, "Failed to Issue a PM command\n"); + goto ret; + } + + if (pmu_wait_done()) + goto ret; + + lss_s0i3_enabled = + ((pmu_read_sss() & S0I3_SSS_TARGET) == S0I3_SSS_TARGET); +ret: + spin_unlock(&mrst_pmu_power_state_lock); + return status; +} + +#ifdef CONFIG_DEBUG_FS +static char *d0ix_names[] = {"D0", "D0i1", "D0i2", "D0i3"}; + +static inline const char *d0ix_name(int state) +{ + return d0ix_names[(int) state]; +} + +static int debug_mrst_pmu_show(struct seq_file *s, void *unused) +{ + struct pci_dev *pdev = NULL; + u32 cur_pmsss; + int lss; + + seq_printf(s, "0x%08X D0I1_ACG_SSS_TARGET\n", D0I1_ACG_SSS_TARGET); + + cur_pmsss = pmu_read_sss(); + + seq_printf(s, "0x%08X S0I3_SSS_TARGET\n", S0I3_SSS_TARGET); + + seq_printf(s, "0x%08X Current SSS ", cur_pmsss); + seq_printf(s, lss_s0i3_enabled ? "\n" : "[BLOCKS s0i3]\n"); + + if (cpumask_equal(cpu_online_mask, cpumask_of(0))) + seq_printf(s, "cpu0 is only cpu online\n"); + else + seq_printf(s, "cpu0 is NOT only cpu online [BLOCKS S0i3]\n"); + + seq_printf(s, "GFX: %s\n", graphics_is_off ? "" : "[BLOCKS s0i3]"); + + + for_each_pci_dev(pdev) { + int pos; + u16 pmcsr; + struct mrst_device *mrst_dev; + int i; + + mrst_dev = pci_id_2_mrst_dev(pdev->device); + + seq_printf(s, "%s %04x/%04X %-16.16s ", + dev_name(&pdev->dev), + pdev->vendor, pdev->device, + dev_driver_string(&pdev->dev)); + + if (unlikely (!mrst_dev)) { + seq_printf(s, " UNKNOWN\n"); + continue; + } + + if (mrst_dev->lss) + seq_printf(s, "LSS %2d %-4s ", mrst_dev->lss, + d0ix_name(((cur_pmsss >> + (mrst_dev->lss * 2)) & 0x3))); + else + seq_printf(s, " "); + + /* PCI PM config space setting */ + pos = pci_find_capability(pdev, PCI_CAP_ID_PM); + if (pos != 0) { + pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr); + seq_printf(s, "PCI-%-4s", + pci_power_name(pmcsr & PCI_PM_CTRL_STATE_MASK)); + } else { + seq_printf(s, " "); + } + + seq_printf(s, " %s ", pci_power_name(mrst_dev->latest_request)); + for (i = 0; i <= PCI_D3cold; ++i) + seq_printf(s, "%d ", mrst_dev->pci_state_counts[i]); + + if (mrst_dev->lss) { + unsigned int lssmask; + + lssmask = SSMSK(D0i3, mrst_dev->lss); + + if ((lssmask & S0I3_SSS_TARGET) && + ((lssmask & cur_pmsss) != + (lssmask & S0I3_SSS_TARGET))) + seq_printf(s , "[BLOCKS s0i3]"); + } + + seq_printf(s, "\n"); + } + seq_printf(s, "Wake Counters:\n"); + for (lss = 0; lss < MRST_NUM_LSS; ++lss) + seq_printf(s, "LSS%d %d\n", lss, wake_counters[lss]); + + seq_printf(s, "Interrupt Counters:\n"); + seq_printf(s, + "INT_SPURIOUS \t%8u\n" "INT_CMD_DONE \t%8u\n" + "INT_CMD_ERR \t%8u\n" "INT_WAKE_RX \t%8u\n" + "INT_SS_ERROR \t%8u\n" "INT_S0IX_MISS\t%8u\n" + "INT_NO_ACKC6 \t%8u\n" "INT_INVALID \t%8u\n", + pmu_irq_stats[INT_SPURIOUS], pmu_irq_stats[INT_CMD_DONE], + pmu_irq_stats[INT_CMD_ERR], pmu_irq_stats[INT_WAKE_RX], + pmu_irq_stats[INT_SS_ERROR], pmu_irq_stats[INT_S0IX_MISS], + pmu_irq_stats[INT_NO_ACKC6], pmu_irq_stats[INT_INVALID]); + + seq_printf(s, "mrst_pmu_wait_ready_calls %8d\n", + pmu_wait_ready_calls); + seq_printf(s, "mrst_pmu_wait_ready_udelays %8d\n", + pmu_wait_ready_udelays); + seq_printf(s, "mrst_pmu_wait_ready_udelays_max %8d\n", + pmu_wait_ready_udelays_max); + seq_printf(s, "mrst_pmu_wait_done_calls %8d\n", + pmu_wait_done_calls); + seq_printf(s, "mrst_pmu_wait_done_udelays %8d\n", + pmu_wait_done_udelays); + seq_printf(s, "mrst_pmu_wait_done_udelays_max %8d\n", + pmu_wait_done_udelays_max); + seq_printf(s, "mrst_pmu_set_power_state_entry %8d\n", + pmu_set_power_state_entry); + seq_printf(s, "mrst_pmu_set_power_state_send_cmd %8d\n", + pmu_set_power_state_send_cmd); + seq_printf(s, "SCU busy: %d\n", pmu_read_busy_status()); + + return 0; +} + +static int debug_mrst_pmu_open(struct inode *inode, struct file *file) +{ + return single_open(file, debug_mrst_pmu_show, NULL); +} + +static const struct file_operations devices_state_operations = { + .open = debug_mrst_pmu_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif /* DEBUG_FS */ + +/* + * Validate SCU PCI shim PCI vendor capability byte + * against LSS hard-coded in mrst_devs[] above. + * DEBUG only. + */ +static void pmu_scu_firmware_debug(void) +{ + struct pci_dev *pdev = NULL; + + for_each_pci_dev(pdev) { + struct mrst_device *mrst_dev; + u8 pci_config_lss; + int pos; + + mrst_dev = pci_id_2_mrst_dev(pdev->device); + if (unlikely(!mrst_dev)) { + printk(KERN_ERR FW_BUG "pmu: Unknown " + "PCI device 0x%04X\n", pdev->device); + continue; + } + + if (mrst_dev->lss == 0) + continue; /* no LSS in our table */ + + pos = pci_find_capability(pdev, PCI_CAP_ID_VNDR); + if (!pos != 0) { + printk(KERN_ERR FW_BUG "pmu: 0x%04X " + "missing PCI Vendor Capability\n", + pdev->device); + continue; + } + pci_read_config_byte(pdev, pos + 4, &pci_config_lss); + if (!(pci_config_lss & PCI_VENDOR_CAP_LOG_SS_MASK)) { + printk(KERN_ERR FW_BUG "pmu: 0x%04X " + "invalid PCI Vendor Capability 0x%x " + " expected LSS 0x%X\n", + pdev->device, pci_config_lss, mrst_dev->lss); + continue; + } + pci_config_lss &= PCI_VENDOR_CAP_LOG_ID_MASK; + + if (mrst_dev->lss == pci_config_lss) + continue; + + printk(KERN_ERR FW_BUG "pmu: 0x%04X LSS = %d, expected %d\n", + pdev->device, pci_config_lss, mrst_dev->lss); + } +} + +/** + * pmu_probe + */ +static int __devinit pmu_probe(struct pci_dev *pdev, + const struct pci_device_id *pci_id) +{ + int ret; + struct mrst_pmu_reg *pmu; + + /* Init the device */ + ret = pci_enable_device(pdev); + if (ret) { + dev_err(&pdev->dev, "Unable to Enable PCI device\n"); + return ret; + } + + ret = pci_request_regions(pdev, MRST_PMU_DRV_NAME); + if (ret < 0) { + dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting\n"); + goto out_err1; + } + + /* Map the memory of PMU reg base */ + pmu = pci_iomap(pdev, 0, 0); + if (!pmu) { + dev_err(&pdev->dev, "Unable to map the PMU address space\n"); + ret = -ENOMEM; + goto out_err2; + } + +#ifdef CONFIG_DEBUG_FS + /* /sys/kernel/debug/mrst_pmu */ + (void) debugfs_create_file("mrst_pmu", S_IFREG | S_IRUGO, + NULL, NULL, &devices_state_operations); +#endif + pmu_reg = pmu; /* success */ + + if (request_irq(pdev->irq, pmu_irq, 0, MRST_PMU_DRV_NAME, NULL)) { + dev_err(&pdev->dev, "Registering isr has failed\n"); + ret = -1; + goto out_err3; + } + + pmu_scu_firmware_debug(); + + pmu_write_wkc(S0I3_WAKE_SOURCES); /* Enable S0i3 wakeup sources */ + + pmu_wait_ready(); + + pmu_write_ssc(D0I1_ACG_SSS_TARGET); /* Enable Auto-Clock_Gating */ + pmu_write_cmd(0x201); + + spin_lock_init(&mrst_pmu_power_state_lock); + + /* Enable the hardware interrupt */ + pmu_irq_enable(); + return 0; + +out_err3: + free_irq(pdev->irq, NULL); + pci_iounmap(pdev, pmu_reg); + pmu_reg = NULL; +out_err2: + pci_release_region(pdev, 0); +out_err1: + pci_disable_device(pdev); + return ret; +} + +static void __devexit pmu_remove(struct pci_dev *pdev) +{ + dev_err(&pdev->dev, "Mid PM pmu_remove called\n"); + + /* Freeing up the irq */ + free_irq(pdev->irq, NULL); + + pci_iounmap(pdev, pmu_reg); + pmu_reg = NULL; + + /* disable the current PCI device */ + pci_release_region(pdev, 0); + pci_disable_device(pdev); +} + +static DEFINE_PCI_DEVICE_TABLE(pmu_pci_ids) = { + { PCI_VDEVICE(INTEL, PCI_DEV_ID_MRST_PMU), 0 }, + { } +}; + +MODULE_DEVICE_TABLE(pci, pmu_pci_ids); + +static struct pci_driver driver = { + .name = MRST_PMU_DRV_NAME, + .id_table = pmu_pci_ids, + .probe = pmu_probe, + .remove = __devexit_p(pmu_remove), +}; + +/** + * pmu_pci_register - register the PMU driver as PCI device + */ +static int __init pmu_pci_register(void) +{ + return pci_register_driver(&driver); +} + +/* Register and probe via fs_initcall() to preceed device_initcall() */ +fs_initcall(pmu_pci_register); + +static void __exit mid_pci_cleanup(void) +{ + pci_unregister_driver(&driver); +} + +static int ia_major; +static int ia_minor; + +static int pmu_sfi_parse_oem(struct sfi_table_header *table) +{ + struct sfi_table_simple *sb; + + sb = (struct sfi_table_simple *)table; + ia_major = (sb->pentry[1] >> 0) & 0xFFFF; + ia_minor = (sb->pentry[1] >> 16) & 0xFFFF; + printk(KERN_INFO "mrst_pmu: IA FW version v%x.%x\n", + ia_major, ia_minor); + + return 0; +} + +static int __init scu_fw_check(void) +{ + int ret; + u32 fw_version; + + if (!pmu_reg) + return 0; /* this driver didn't probe-out */ + + sfi_table_parse("OEMB", NULL, NULL, pmu_sfi_parse_oem); + + if (ia_major < 0x6005 || ia_minor < 0x1525) { + WARN(1, "mrst_pmu: IA FW version too old\n"); + return -1; + } + + ret = intel_scu_ipc_command(IPCMSG_FW_REVISION, 0, NULL, 0, + &fw_version, 1); + + if (ret) { + WARN(1, "mrst_pmu: IPC FW version? %d\n", ret); + } else { + int scu_major = (fw_version >> 8) & 0xFF; + int scu_minor = (fw_version >> 0) & 0xFF; + + printk(KERN_INFO "mrst_pmu: firmware v%x\n", fw_version); + + if ((scu_major >= 0xC0) && (scu_minor >= 0x49)) { + printk(KERN_INFO "mrst_pmu: enabling S0i3\n"); + mrst_pmu_s0i3_enable = true; + } else { + WARN(1, "mrst_pmu: S0i3 disabled, old firmware %X.%X", + scu_major, scu_minor); + } + } + return 0; +} +late_initcall(scu_fw_check); +module_exit(mid_pci_cleanup); diff --git a/arch/x86/platform/mrst/pmu.h b/arch/x86/platform/mrst/pmu.h new file mode 100644 index 000000000000..bfbfe64b167b --- /dev/null +++ b/arch/x86/platform/mrst/pmu.h @@ -0,0 +1,234 @@ +/* + * mrst/pmu.h - private definitions for MRST Power Management Unit mrst/pmu.c + * + * Copyright (c) 2011, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef _MRST_PMU_H_ +#define _MRST_PMU_H_ + +#define PCI_DEV_ID_MRST_PMU 0x0810 +#define MRST_PMU_DRV_NAME "mrst_pmu" +#define PCI_SUB_CLASS_MASK 0xFF00 + +#define PCI_VENDOR_CAP_LOG_ID_MASK 0x7F +#define PCI_VENDOR_CAP_LOG_SS_MASK 0x80 + +#define SUB_SYS_ALL_D0I1 0x01155555 +#define S0I3_WAKE_SOURCES 0x00001FFF + +#define PM_S0I3_COMMAND \ + ((0 << 31) | /* Reserved */ \ + (0 << 30) | /* Core must be idle */ \ + (0xc2 << 22) | /* ACK C6 trigger */ \ + (3 << 19) | /* Trigger on DMI message */ \ + (3 << 16) | /* Enter S0i3 */ \ + (0 << 13) | /* Numeric mode ID (sw) */ \ + (3 << 9) | /* Trigger mode */ \ + (0 << 8) | /* Do not interrupt */ \ + (1 << 0)) /* Set configuration */ + +#define LSS_DMI 0 +#define LSS_SD_HC0 1 +#define LSS_SD_HC1 2 +#define LSS_NAND 3 +#define LSS_IMAGING 4 +#define LSS_SECURITY 5 +#define LSS_DISPLAY 6 +#define LSS_USB_HC 7 +#define LSS_USB_OTG 8 +#define LSS_AUDIO 9 +#define LSS_AUDIO_LPE 9 +#define LSS_AUDIO_SSP 9 +#define LSS_I2C0 10 +#define LSS_I2C1 10 +#define LSS_I2C2 10 +#define LSS_KBD 10 +#define LSS_SPI0 10 +#define LSS_SPI1 10 +#define LSS_SPI2 10 +#define LSS_GPIO 10 +#define LSS_SRAM 11 /* used by SCU, do not touch */ +#define LSS_SD_HC2 12 +/* LSS hardware bits 15,14,13 are hardwired to 0, thus unusable */ +#define MRST_NUM_LSS 13 + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#define SSMSK(mask, lss) ((mask) << ((lss) * 2)) +#define D0 0 +#define D0i1 1 +#define D0i2 2 +#define D0i3 3 + +#define S0I3_SSS_TARGET ( \ + SSMSK(D0i1, LSS_DMI) | \ + SSMSK(D0i3, LSS_SD_HC0) | \ + SSMSK(D0i3, LSS_SD_HC1) | \ + SSMSK(D0i3, LSS_NAND) | \ + SSMSK(D0i3, LSS_SD_HC2) | \ + SSMSK(D0i3, LSS_IMAGING) | \ + SSMSK(D0i3, LSS_SECURITY) | \ + SSMSK(D0i3, LSS_DISPLAY) | \ + SSMSK(D0i3, LSS_USB_HC) | \ + SSMSK(D0i3, LSS_USB_OTG) | \ + SSMSK(D0i3, LSS_AUDIO) | \ + SSMSK(D0i1, LSS_I2C0)) + +/* + * D0i1 on Langwell is Autonomous Clock Gating (ACG). + * Enable ACG on every LSS except camera and audio + */ +#define D0I1_ACG_SSS_TARGET \ + (SUB_SYS_ALL_D0I1 & ~SSMSK(D0i1, LSS_IMAGING) & ~SSMSK(D0i1, LSS_AUDIO)) + +enum cm_mode { + CM_NOP, /* ignore the config mode value */ + CM_IMMEDIATE, + CM_DELAY, + CM_TRIGGER, + CM_INVALID +}; + +enum sys_state { + SYS_STATE_S0I0, + SYS_STATE_S0I1, + SYS_STATE_S0I2, + SYS_STATE_S0I3, + SYS_STATE_S3, + SYS_STATE_S5 +}; + +#define SET_CFG_CMD 1 + +enum int_status { + INT_SPURIOUS = 0, + INT_CMD_DONE = 1, + INT_CMD_ERR = 2, + INT_WAKE_RX = 3, + INT_SS_ERROR = 4, + INT_S0IX_MISS = 5, + INT_NO_ACKC6 = 6, + INT_INVALID = 7, +}; + +/* PMU register interface */ +static struct mrst_pmu_reg { + u32 pm_sts; /* 0x00 */ + u32 pm_cmd; /* 0x04 */ + u32 pm_ics; /* 0x08 */ + u32 _resv1; /* 0x0C */ + u32 pm_wkc[2]; /* 0x10 */ + u32 pm_wks[2]; /* 0x18 */ + u32 pm_ssc[4]; /* 0x20 */ + u32 pm_sss[4]; /* 0x30 */ + u32 pm_wssc[4]; /* 0x40 */ + u32 pm_c3c4; /* 0x50 */ + u32 pm_c5c6; /* 0x54 */ + u32 pm_msi_disable; /* 0x58 */ +} *pmu_reg; + +static inline u32 pmu_read_sts(void) { return readl(&pmu_reg->pm_sts); } +static inline u32 pmu_read_ics(void) { return readl(&pmu_reg->pm_ics); } +static inline u32 pmu_read_wks(void) { return readl(&pmu_reg->pm_wks[0]); } +static inline u32 pmu_read_sss(void) { return readl(&pmu_reg->pm_sss[0]); } + +static inline void pmu_write_cmd(u32 arg) { writel(arg, &pmu_reg->pm_cmd); } +static inline void pmu_write_ics(u32 arg) { writel(arg, &pmu_reg->pm_ics); } +static inline void pmu_write_wkc(u32 arg) { writel(arg, &pmu_reg->pm_wkc[0]); } +static inline void pmu_write_ssc(u32 arg) { writel(arg, &pmu_reg->pm_ssc[0]); } +static inline void pmu_write_wssc(u32 arg) + { writel(arg, &pmu_reg->pm_wssc[0]); } + +static inline void pmu_msi_enable(void) { writel(0, &pmu_reg->pm_msi_disable); } +static inline u32 pmu_msi_is_disabled(void) + { return readl(&pmu_reg->pm_msi_disable); } + +union pmu_pm_ics { + struct { + u32 cause:8; + u32 enable:1; + u32 pending:1; + u32 reserved:22; + } bits; + u32 value; +}; + +static inline void pmu_irq_enable(void) +{ + union pmu_pm_ics pmu_ics; + + pmu_ics.value = pmu_read_ics(); + pmu_ics.bits.enable = 1; + pmu_write_ics(pmu_ics.value); +} + +union pmu_pm_status { + struct { + u32 pmu_rev:8; + u32 pmu_busy:1; + u32 mode_id:4; + u32 Reserved:19; + } pmu_status_parts; + u32 pmu_status_value; +}; + +static inline int pmu_read_busy_status(void) +{ + union pmu_pm_status result; + + result.pmu_status_value = pmu_read_sts(); + + return result.pmu_status_parts.pmu_busy; +} + +/* pmu set config parameters */ +struct cfg_delay_param_t { + u32 cmd:8; + u32 ioc:1; + u32 cfg_mode:4; + u32 mode_id:3; + u32 sys_state:3; + u32 cfg_delay:8; + u32 rsvd:5; +}; + +struct cfg_trig_param_t { + u32 cmd:8; + u32 ioc:1; + u32 cfg_mode:4; + u32 mode_id:3; + u32 sys_state:3; + u32 cfg_trig_type:3; + u32 cfg_trig_val:8; + u32 cmbi:1; + u32 rsvd1:1; +}; + +union pmu_pm_set_cfg_cmd_t { + union { + struct cfg_delay_param_t d_param; + struct cfg_trig_param_t t_param; + } pmu2_params; + u32 pmu_pm_set_cfg_cmd_value; +}; + +#ifdef FUTURE_PATCH +extern int mrst_s0i3_entry(u32 regval, u32 *regaddr); +#else +static inline int mrst_s0i3_entry(u32 regval, u32 *regaddr) { return -1; } +#endif +#endif -- cgit v1.2.3 From a3ea14df0e383f44dcb2e61badb71180dbffe526 Mon Sep 17 00:00:00 2001 From: Paul Fox Date: Tue, 26 Jul 2011 16:42:26 +0100 Subject: x86, olpc: Wait for last byte of EC command to be accepted When executing EC commands, only waiting when there are still more bytes to write is usually fine. However, if the system suspends very quickly after a call to olpc_ec_cmd(), the last data byte may not yet be transferred to the EC, and the command will not complete. This solves a bug where the SCI wakeup mask was not correctly written when going into suspend. It means that sometimes, on XO-1.5 (but not XO-1), the devices that were marked as wakeup sources can't wake up the system. e.g. you ask for wifi wakeups, suspend, but then incoming wifi frames don't wake up the system as they should. Signed-off-by: Paul Fox Signed-off-by: Daniel Drake Acked-by: Andres Salomon Cc: Signed-off-by: Ingo Molnar --- arch/x86/platform/olpc/olpc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c index 8b9940e78e2f..7cce722667b8 100644 --- a/arch/x86/platform/olpc/olpc.c +++ b/arch/x86/platform/olpc/olpc.c @@ -161,13 +161,13 @@ restart: if (inbuf && inlen) { /* write data to EC */ for (i = 0; i < inlen; i++) { + pr_devel("olpc-ec: sending cmd arg 0x%x\n", inbuf[i]); + outb(inbuf[i], 0x68); if (wait_on_ibf(0x6c, 0)) { printk(KERN_ERR "olpc-ec: timeout waiting for" " EC accept data!\n"); goto err; } - pr_devel("olpc-ec: sending cmd arg 0x%x\n", inbuf[i]); - outb(inbuf[i], 0x68); } } if (outbuf && outlen) { -- cgit v1.2.3 From a94cc4e6c0a26a7c8f79a432ab2c89534aa674d5 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Fri, 26 Aug 2011 12:20:59 +0100 Subject: sfi: table irq 0xFF means 'no interrupt' According to the SFI specification irq number 0xFF means device has no interrupt or interrupt attached via GPIO. Currently, we don't handle this special case and set irq field in *_board_info structs to 255. It leads to confusion in some drivers. Accelerometer driver tries to register interrupt 255, fails and prints "Cannot get IRQ" to dmesg. Signed-off-by: Kirill A. Shutemov Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- arch/x86/platform/mrst/mrst.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index 7000e74b3087..58425adc22c6 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -689,7 +689,9 @@ static int __init sfi_parse_devs(struct sfi_table_header *table) irq_attr.trigger = 1; irq_attr.polarity = 1; io_apic_set_pci_routing(NULL, pentry->irq, &irq_attr); - } + } else + pentry->irq = 0; /* No irq */ + switch (pentry->type) { case SFI_DEV_TYPE_IPC: /* ID as IRQ is a hack that will go away */ -- cgit v1.2.3 From efe3ed9837fd2f9679659673f1d2078f1597bf18 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Fri, 26 Aug 2011 11:25:14 +0100 Subject: x86/mrst: Add platform data for Max3110 devices Those info will be used when spi controller driver setup max3110 as a slave device Signed-off-by: Feng Tang Signed-off-by: Alan Cox Signed-off-by: Dirk Brandewie Signed-off-by: Greg Kroah-Hartman --- arch/x86/platform/mrst/mrst.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index 7000e74b3087..b8da1b968998 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -14,6 +14,8 @@ #include #include +#include +#include #include #include #include @@ -392,6 +394,7 @@ static void __init *max3111_platform_data(void *info) struct spi_board_info *spi_info = info; int intr = get_gpio_by_name("max3111_int"); + spi_info->mode = SPI_MODE_0; if (intr == -1) return NULL; spi_info->irq = intr + MRST_IRQ_OFFSET; -- cgit v1.2.3 From d4f3e350172a1dc769ed5e7f5bd540feb0c475d8 Mon Sep 17 00:00:00 2001 From: Ed Wildgoose Date: Tue, 20 Sep 2011 14:00:12 -0700 Subject: x86: geode: New PCEngines Alix system driver This new driver replaces the old PCEngines Alix 2/3 LED driver with a new driver that controls the LEDs through the leds-gpio driver. The old driver accessed GPIOs directly, which created a conflict and prevented also loading the cs5535-gpio driver to read other GPIOs on the Alix board. With this new driver, we hook into leds-gpio which in turn uses GPIO to control the LEDs and therefore it's possible to control both the LEDs and access onboard GPIOs Driver is moved to platform/geode as requested by Grant and any other geode initialisation modules should move here also This driver is inspired by leds-net5501.c by Alessandro Zummo. Ideally, leds-net5501.c should also be moved to platform/geode. Additionally the driver relies on parts of the patch: 7f131cf3ed ("leds: leds-alix2c - take port address from MSR) by Daniel Mack to perform detection of the Alix board. [akpm@linux-foundation.org: include module.h] Signed-off-by: Ed Wildgoose Cc: git@wildgooses.com Cc: Alessandro Zummo Cc: Daniel Mack Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Richard Purdie Reviewed-by: Grant Likely Signed-off-by: Andrew Morton Signed-off-by: Thomas Gleixner --- arch/x86/Kconfig | 14 +++ arch/x86/platform/Makefile | 1 + arch/x86/platform/geode/Makefile | 1 + arch/x86/platform/geode/alix.c | 142 +++++++++++++++++++++++ drivers/leds/Kconfig | 8 -- drivers/leds/Makefile | 1 - drivers/leds/leds-alix2.c | 239 --------------------------------------- 7 files changed, 158 insertions(+), 248 deletions(-) create mode 100644 arch/x86/platform/geode/Makefile create mode 100644 arch/x86/platform/geode/alix.c delete mode 100644 drivers/leds/leds-alix2.c (limited to 'arch/x86/platform') diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 6a47bb22657f..8c4e5bc35e29 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -2064,6 +2064,20 @@ config OLPC_XO15_SCI - AC adapter status updates - Battery status updates +config ALIX + bool "PCEngines ALIX System Support (LED setup)" + select GPIOLIB + ---help--- + This option enables system support for the PCEngines ALIX. + At present this just sets up LEDs for GPIO control on + ALIX2/3/6 boards. However, other system specific setup should + get added here. + + Note: You must still enable the drivers for GPIO and LED support + (GPIO_CS5535 & LEDS_GPIO) to actually use the LEDs + + Note: You have to set alix.force=1 for boards with Award BIOS. + endif # X86_32 config AMD_NB diff --git a/arch/x86/platform/Makefile b/arch/x86/platform/Makefile index 021eee91c056..8d874396cb29 100644 --- a/arch/x86/platform/Makefile +++ b/arch/x86/platform/Makefile @@ -1,6 +1,7 @@ # Platform specific code goes here obj-y += ce4100/ obj-y += efi/ +obj-y += geode/ obj-y += iris/ obj-y += mrst/ obj-y += olpc/ diff --git a/arch/x86/platform/geode/Makefile b/arch/x86/platform/geode/Makefile new file mode 100644 index 000000000000..07c9cd05021a --- /dev/null +++ b/arch/x86/platform/geode/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_ALIX) += alix.o diff --git a/arch/x86/platform/geode/alix.c b/arch/x86/platform/geode/alix.c new file mode 100644 index 000000000000..ca1973699d3d --- /dev/null +++ b/arch/x86/platform/geode/alix.c @@ -0,0 +1,142 @@ +/* + * System Specific setup for PCEngines ALIX. + * At the moment this means setup of GPIO control of LEDs + * on Alix.2/3/6 boards. + * + * + * Copyright (C) 2008 Constantin Baranov + * Copyright (C) 2011 Ed Wildgoose + * + * TODO: There are large similarities with leds-net5501.c + * by Alessandro Zummo + * In the future leds-net5501.c should be migrated over to platform + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int force = 0; +module_param(force, bool, 0444); +/* FIXME: Award bios is not automatically detected as Alix platform */ +MODULE_PARM_DESC(force, "Force detection as ALIX.2/ALIX.3 platform"); + +static struct gpio_led alix_leds[] = { + { + .name = "alix:1", + .gpio = 6, + .default_trigger = "default-on", + .active_low = 1, + }, + { + .name = "alix:2", + .gpio = 25, + .default_trigger = "default-off", + .active_low = 1, + }, + { + .name = "alix:3", + .gpio = 27, + .default_trigger = "default-off", + .active_low = 1, + }, +}; + +static struct gpio_led_platform_data alix_leds_data = { + .num_leds = ARRAY_SIZE(alix_leds), + .leds = alix_leds, +}; + +static struct platform_device alix_leds_dev = { + .name = "leds-gpio", + .id = -1, + .dev.platform_data = &alix_leds_data, +}; + +static void __init register_alix(void) +{ + /* Setup LED control through leds-gpio driver */ + platform_device_register(&alix_leds_dev); +} + +static int __init alix_present(unsigned long bios_phys, + const char *alix_sig, + size_t alix_sig_len) +{ + const size_t bios_len = 0x00010000; + const char *bios_virt; + const char *scan_end; + const char *p; + char name[64]; + + if (force) { + printk(KERN_NOTICE "%s: forced to skip BIOS test, " + "assume system is ALIX.2/ALIX.3\n", + KBUILD_MODNAME); + return 1; + } + + bios_virt = phys_to_virt(bios_phys); + scan_end = bios_virt + bios_len - (alix_sig_len + 2); + for (p = bios_virt; p < scan_end; p++) { + const char *tail; + char *a; + + if (memcmp(p, alix_sig, alix_sig_len) != 0) + continue; + + memcpy(name, p, sizeof(name)); + + /* remove the first \0 character from string */ + a = strchr(name, '\0'); + if (a) + *a = ' '; + + /* cut the string at a newline */ + a = strchr(name, '\r'); + if (a) + *a = '\0'; + + tail = p + alix_sig_len; + if ((tail[0] == '2' || tail[0] == '3')) { + printk(KERN_INFO + "%s: system is recognized as \"%s\"\n", + KBUILD_MODNAME, name); + return 1; + } + } + + return 0; +} + +static int __init alix_init(void) +{ + const char tinybios_sig[] = "PC Engines ALIX."; + const char coreboot_sig[] = "PC Engines\0ALIX."; + + if (!is_geode()) + return 0; + + if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) || + alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1)) + register_alix(); + + return 0; +} + +module_init(alix_init); + +MODULE_AUTHOR("Ed Wildgoose "); +MODULE_DESCRIPTION("PCEngines ALIX System Setup"); +MODULE_LICENSE("GPL"); diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index b591e726a6fa..8974d274334f 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -113,14 +113,6 @@ config LEDS_WRAP help This option enables support for the PCEngines WRAP programmable LEDs. -config LEDS_ALIX2 - tristate "LED Support for ALIX.2 and ALIX.3 series" - depends on LEDS_CLASS - depends on X86 && !GPIO_CS5535 && !CS5535_GPIO - help - This option enables support for the PCEngines ALIX.2 and ALIX.3 LEDs. - You have to set leds-alix2.force=1 for boards with Award BIOS. - config LEDS_COBALT_QUBE tristate "LED Support for the Cobalt Qube series front LED" depends on LEDS_CLASS diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile index bbfd2e367dc0..a0a1b89d78a8 100644 --- a/drivers/leds/Makefile +++ b/drivers/leds/Makefile @@ -16,7 +16,6 @@ obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o obj-$(CONFIG_LEDS_NET5501) += leds-net5501.o obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o -obj-$(CONFIG_LEDS_ALIX2) += leds-alix2.o obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o obj-$(CONFIG_LEDS_SUNFIRE) += leds-sunfire.o diff --git a/drivers/leds/leds-alix2.c b/drivers/leds/leds-alix2.c deleted file mode 100644 index f59ffadf5125..000000000000 --- a/drivers/leds/leds-alix2.c +++ /dev/null @@ -1,239 +0,0 @@ -/* - * LEDs driver for PCEngines ALIX.2 and ALIX.3 - * - * Copyright (C) 2008 Constantin Baranov - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static int force = 0; -module_param(force, bool, 0444); -MODULE_PARM_DESC(force, "Assume system has ALIX.2/ALIX.3 style LEDs"); - -#define MSR_LBAR_GPIO 0x5140000C -#define CS5535_GPIO_SIZE 256 - -static u32 gpio_base; - -static struct pci_device_id divil_pci[] = { - { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, - { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) }, - { } /* NULL entry */ -}; -MODULE_DEVICE_TABLE(pci, divil_pci); - -struct alix_led { - struct led_classdev cdev; - unsigned short port; - unsigned int on_value; - unsigned int off_value; -}; - -static void alix_led_set(struct led_classdev *led_cdev, - enum led_brightness brightness) -{ - struct alix_led *led_dev = - container_of(led_cdev, struct alix_led, cdev); - - if (brightness) - outl(led_dev->on_value, gpio_base + led_dev->port); - else - outl(led_dev->off_value, gpio_base + led_dev->port); -} - -static struct alix_led alix_leds[] = { - { - .cdev = { - .name = "alix:1", - .brightness_set = alix_led_set, - }, - .port = 0x00, - .on_value = 1 << 22, - .off_value = 1 << 6, - }, - { - .cdev = { - .name = "alix:2", - .brightness_set = alix_led_set, - }, - .port = 0x80, - .on_value = 1 << 25, - .off_value = 1 << 9, - }, - { - .cdev = { - .name = "alix:3", - .brightness_set = alix_led_set, - }, - .port = 0x80, - .on_value = 1 << 27, - .off_value = 1 << 11, - }, -}; - -static int __init alix_led_probe(struct platform_device *pdev) -{ - int i; - int ret; - - for (i = 0; i < ARRAY_SIZE(alix_leds); i++) { - alix_leds[i].cdev.flags |= LED_CORE_SUSPENDRESUME; - ret = led_classdev_register(&pdev->dev, &alix_leds[i].cdev); - if (ret < 0) - goto fail; - } - return 0; - -fail: - while (--i >= 0) - led_classdev_unregister(&alix_leds[i].cdev); - return ret; -} - -static int alix_led_remove(struct platform_device *pdev) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(alix_leds); i++) - led_classdev_unregister(&alix_leds[i].cdev); - return 0; -} - -static struct platform_driver alix_led_driver = { - .remove = alix_led_remove, - .driver = { - .name = KBUILD_MODNAME, - .owner = THIS_MODULE, - }, -}; - -static int __init alix_present(unsigned long bios_phys, - const char *alix_sig, - size_t alix_sig_len) -{ - const size_t bios_len = 0x00010000; - const char *bios_virt; - const char *scan_end; - const char *p; - char name[64]; - - if (force) { - printk(KERN_NOTICE "%s: forced to skip BIOS test, " - "assume system has ALIX.2 style LEDs\n", - KBUILD_MODNAME); - return 1; - } - - bios_virt = phys_to_virt(bios_phys); - scan_end = bios_virt + bios_len - (alix_sig_len + 2); - for (p = bios_virt; p < scan_end; p++) { - const char *tail; - char *a; - - if (memcmp(p, alix_sig, alix_sig_len) != 0) - continue; - - memcpy(name, p, sizeof(name)); - - /* remove the first \0 character from string */ - a = strchr(name, '\0'); - if (a) - *a = ' '; - - /* cut the string at a newline */ - a = strchr(name, '\r'); - if (a) - *a = '\0'; - - tail = p + alix_sig_len; - if ((tail[0] == '2' || tail[0] == '3')) { - printk(KERN_INFO - "%s: system is recognized as \"%s\"\n", - KBUILD_MODNAME, name); - return 1; - } - } - - return 0; -} - -static struct platform_device *pdev; - -static int __init alix_pci_led_init(void) -{ - u32 low, hi; - - if (pci_dev_present(divil_pci) == 0) { - printk(KERN_WARNING KBUILD_MODNAME": DIVIL not found\n"); - return -ENODEV; - } - - /* Grab the GPIO I/O range */ - rdmsr(MSR_LBAR_GPIO, low, hi); - - /* Check the mask and whether GPIO is enabled (sanity check) */ - if (hi != 0x0000f001) { - printk(KERN_WARNING KBUILD_MODNAME": GPIO not enabled\n"); - return -ENODEV; - } - - /* Mask off the IO base address */ - gpio_base = low & 0x0000ff00; - - if (!request_region(gpio_base, CS5535_GPIO_SIZE, KBUILD_MODNAME)) { - printk(KERN_ERR KBUILD_MODNAME": can't allocate I/O for GPIO\n"); - return -ENODEV; - } - - /* Set GPIO function to output */ - outl(1 << 6, gpio_base + 0x04); - outl(1 << 9, gpio_base + 0x84); - outl(1 << 11, gpio_base + 0x84); - - return 0; -} - -static int __init alix_led_init(void) -{ - int ret = -ENODEV; - const char tinybios_sig[] = "PC Engines ALIX."; - const char coreboot_sig[] = "PC Engines\0ALIX."; - - if (alix_present(0xf0000, tinybios_sig, sizeof(tinybios_sig) - 1) || - alix_present(0x500, coreboot_sig, sizeof(coreboot_sig) - 1)) - ret = alix_pci_led_init(); - - if (ret < 0) - return ret; - - pdev = platform_device_register_simple(KBUILD_MODNAME, -1, NULL, 0); - if (!IS_ERR(pdev)) { - ret = platform_driver_probe(&alix_led_driver, alix_led_probe); - if (ret) - platform_device_unregister(pdev); - } else - ret = PTR_ERR(pdev); - - return ret; -} - -static void __exit alix_led_exit(void) -{ - platform_device_unregister(pdev); - platform_driver_unregister(&alix_led_driver); - release_region(gpio_base, CS5535_GPIO_SIZE); -} - -module_init(alix_led_init); -module_exit(alix_led_exit); - -MODULE_AUTHOR("Constantin Baranov "); -MODULE_DESCRIPTION("PCEngines ALIX.2 and ALIX.3 LED driver"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 6a469e4665bc158599de55d64388861d0a9f10f4 Mon Sep 17 00:00:00 2001 From: Jack Steiner Date: Tue, 20 Sep 2011 13:55:04 -0700 Subject: x86: uv2: Workaround for UV2 Hub bug (system global address format) This is a workaround for a UV2 hub bug that affects the format of system global addresses. The GRU API for UV2 was inadvertently broken by a hardware change. The format of the physical address used for TLB dropins and for addresses used with instructions running in unmapped mode has changed. This change was not documented and became apparent only when diags failed running on system simulators. For UV1, TLB and GRU instruction physical addresses are identical to socket physical addresses (although high NASID bits must be OR'ed into the address). For UV2, socket physical addresses need to be converted. The NODE portion of the physical address needs to be shifted so that the low bit is in bit 39 or bit 40, depending on an MMR value. It is not yet clear if this bug will be fixed in a silicon respin. If it is fixed, the hub revision will be incremented & the workaround disabled. Signed-off-by: Jack Steiner Cc: Ingo Molnar Cc: "H. Peter Anvin" Cc: Signed-off-by: Andrew Morton Signed-off-by: Thomas Gleixner --- arch/x86/include/asm/uv/uv_bau.h | 1 + arch/x86/include/asm/uv/uv_hub.h | 37 ++++++++++++++++++++++++++++++++++--- arch/x86/kernel/apic/x2apic_uv_x.c | 7 +++++-- arch/x86/platform/uv/tlb_uv.c | 17 ++++++----------- 4 files changed, 46 insertions(+), 16 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/include/asm/uv/uv_bau.h b/arch/x86/include/asm/uv/uv_bau.h index 37d369859c8e..0c767a8e000e 100644 --- a/arch/x86/include/asm/uv/uv_bau.h +++ b/arch/x86/include/asm/uv/uv_bau.h @@ -55,6 +55,7 @@ #define UV_BAU_TUNABLES_DIR "sgi_uv" #define UV_BAU_TUNABLES_FILE "bau_tunables" #define WHITESPACE " \t\n" +#define uv_mmask ((1UL << uv_hub_info->m_val) - 1) #define uv_physnodeaddr(x) ((__pa((unsigned long)(x)) & uv_mmask)) #define cpubit_isset(cpu, bau_local_cpumask) \ test_bit((cpu), (bau_local_cpumask).bits) diff --git a/arch/x86/include/asm/uv/uv_hub.h b/arch/x86/include/asm/uv/uv_hub.h index f26544a15214..54a13aaebc40 100644 --- a/arch/x86/include/asm/uv/uv_hub.h +++ b/arch/x86/include/asm/uv/uv_hub.h @@ -46,6 +46,13 @@ * PNODE - the low N bits of the GNODE. The PNODE is the most useful variant * of the nasid for socket usage. * + * GPA - (global physical address) a socket physical address converted + * so that it can be used by the GRU as a global address. Socket + * physical addresses 1) need additional NASID (node) bits added + * to the high end of the address, and 2) unaliased if the + * partition does not have a physical address 0. In addition, on + * UV2 rev 1, GPAs need the gnode left shifted to bits 39 or 40. + * * * NumaLink Global Physical Address Format: * +--------------------------------+---------------------+ @@ -141,6 +148,8 @@ struct uv_hub_info_s { unsigned int gnode_extra; unsigned char hub_revision; unsigned char apic_pnode_shift; + unsigned char m_shift; + unsigned char n_lshift; unsigned long gnode_upper; unsigned long lowmem_remap_top; unsigned long lowmem_remap_base; @@ -177,6 +186,16 @@ static inline int is_uv2_hub(void) return uv_hub_info->hub_revision >= UV2_HUB_REVISION_BASE; } +static inline int is_uv2_1_hub(void) +{ + return uv_hub_info->hub_revision == UV2_HUB_REVISION_BASE; +} + +static inline int is_uv2_2_hub(void) +{ + return uv_hub_info->hub_revision == UV2_HUB_REVISION_BASE + 1; +} + union uvh_apicid { unsigned long v; struct uvh_apicid_s { @@ -276,7 +295,10 @@ static inline unsigned long uv_soc_phys_ram_to_gpa(unsigned long paddr) { if (paddr < uv_hub_info->lowmem_remap_top) paddr |= uv_hub_info->lowmem_remap_base; - return paddr | uv_hub_info->gnode_upper; + paddr |= uv_hub_info->gnode_upper; + paddr = ((paddr << uv_hub_info->m_shift) >> uv_hub_info->m_shift) | + ((paddr >> uv_hub_info->m_val) << uv_hub_info->n_lshift); + return paddr; } @@ -300,16 +322,19 @@ static inline unsigned long uv_gpa_to_soc_phys_ram(unsigned long gpa) unsigned long remap_base = uv_hub_info->lowmem_remap_base; unsigned long remap_top = uv_hub_info->lowmem_remap_top; + gpa = ((gpa << uv_hub_info->m_shift) >> uv_hub_info->m_shift) | + ((gpa >> uv_hub_info->n_lshift) << uv_hub_info->m_val); + gpa = gpa & uv_hub_info->gpa_mask; if (paddr >= remap_base && paddr < remap_base + remap_top) paddr -= remap_base; return paddr; } -/* gnode -> pnode */ +/* gpa -> pnode */ static inline unsigned long uv_gpa_to_gnode(unsigned long gpa) { - return gpa >> uv_hub_info->m_val; + return gpa >> uv_hub_info->n_lshift; } /* gpa -> pnode */ @@ -320,6 +345,12 @@ static inline int uv_gpa_to_pnode(unsigned long gpa) return uv_gpa_to_gnode(gpa) & n_mask; } +/* gpa -> node offset*/ +static inline unsigned long uv_gpa_to_offset(unsigned long gpa) +{ + return (gpa << uv_hub_info->m_shift) >> uv_hub_info->m_shift; +} + /* pnode, offset --> socket virtual */ static inline void *uv_pnode_offset_to_vaddr(int pnode, unsigned long offset) { diff --git a/arch/x86/kernel/apic/x2apic_uv_x.c b/arch/x86/kernel/apic/x2apic_uv_x.c index 34b18594e724..cfeb978f49fd 100644 --- a/arch/x86/kernel/apic/x2apic_uv_x.c +++ b/arch/x86/kernel/apic/x2apic_uv_x.c @@ -832,6 +832,10 @@ void __init uv_system_init(void) uv_cpu_hub_info(cpu)->apic_pnode_shift = uvh_apicid.s.pnode_shift; uv_cpu_hub_info(cpu)->hub_revision = uv_hub_info->hub_revision; + uv_cpu_hub_info(cpu)->m_shift = 64 - m_val; + uv_cpu_hub_info(cpu)->n_lshift = is_uv2_1_hub() ? + (m_val == 40 ? 40 : 39) : m_val; + pnode = uv_apicid_to_pnode(apicid); blade = boot_pnode_to_blade(pnode); lcpu = uv_blade_info[blade].nr_possible_cpus; @@ -862,8 +866,7 @@ void __init uv_system_init(void) if (uv_node_to_blade[nid] >= 0) continue; paddr = node_start_pfn(nid) << PAGE_SHIFT; - paddr = uv_soc_phys_ram_to_gpa(paddr); - pnode = (paddr >> m_val) & pnode_mask; + pnode = uv_gpa_to_pnode(uv_soc_phys_ram_to_gpa(paddr)); blade = boot_pnode_to_blade(pnode); uv_node_to_blade[nid] = blade; } diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index db8b915f54bc..5b552198f774 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -115,9 +115,6 @@ early_param("nobau", setup_nobau); /* base pnode in this partition */ static int uv_base_pnode __read_mostly; -/* position of pnode (which is nasid>>1): */ -static int uv_nshift __read_mostly; -static unsigned long uv_mmask __read_mostly; static DEFINE_PER_CPU(struct ptc_stats, ptcstats); static DEFINE_PER_CPU(struct bau_control, bau_control); @@ -1435,7 +1432,7 @@ static void activation_descriptor_init(int node, int pnode, int base_pnode) { int i; int cpu; - unsigned long pa; + unsigned long gpa; unsigned long m; unsigned long n; size_t dsize; @@ -1451,9 +1448,9 @@ static void activation_descriptor_init(int node, int pnode, int base_pnode) bau_desc = kmalloc_node(dsize, GFP_KERNEL, node); BUG_ON(!bau_desc); - pa = uv_gpa(bau_desc); /* need the real nasid*/ - n = pa >> uv_nshift; - m = pa & uv_mmask; + gpa = uv_gpa(bau_desc); + n = uv_gpa_to_gnode(gpa); + m = uv_gpa_to_offset(gpa); /* the 14-bit pnode */ write_mmr_descriptor_base(pnode, (n << UV_DESC_PSHIFT | m)); @@ -1525,9 +1522,9 @@ static void pq_init(int node, int pnode) bcp->queue_last = pqp + (DEST_Q_SIZE - 1); } /* - * need the pnode of where the memory was really allocated + * need the gnode of where the memory was really allocated */ - pn = uv_gpa(pqp) >> uv_nshift; + pn = uv_gpa_to_gnode(uv_gpa(pqp)); first = uv_physnodeaddr(pqp); pn_first = ((unsigned long)pn << UV_PAYLOADQ_PNODE_SHIFT) | first; last = uv_physnodeaddr(pqp + (DEST_Q_SIZE - 1)); @@ -1837,8 +1834,6 @@ static int __init uv_bau_init(void) zalloc_cpumask_var_node(mask, GFP_KERNEL, cpu_to_node(cur_cpu)); } - uv_nshift = uv_hub_info->m_val; - uv_mmask = (1UL << uv_hub_info->m_val) - 1; nuvhubs = uv_num_possible_blades(); spin_lock_init(&disable_lock); congested_cycles = usec_2_cycles(congested_respns_us); -- cgit v1.2.3 From 47997d756aa2a84ab577e1b0383cc12d582fc69c Mon Sep 17 00:00:00 2001 From: Matt Fleming Date: Wed, 21 Sep 2011 16:08:03 +0200 Subject: x86/rtc: Don't recursively acquire rtc_lock A deadlock was introduced on x86 in commit ef68c8f87ed1 ("x86: Serialize EFI time accesses on rtc_lock") because efi_get_time() and friends can be called with rtc_lock already held by read_persistent_time(), e.g.: timekeeping_init() read_persistent_clock() <-- acquire rtc_lock efi_get_time() phys_efi_get_time() <-- acquire rtc_lock To fix this let's push the locking down into the get_wallclock() and set_wallclock() implementations. Only the clock implementations that access the x86 RTC directly need to acquire rtc_lock, so it makes sense to push the locking down into the rtc, vrtc and efi code. The virtualization implementations don't require rtc_lock to be held because they provide their own serialization. Signed-off-by: Matt Fleming Acked-by: Jan Beulich Acked-by: Avi Kivity [for the virtualization aspect] Cc: "H. Peter Anvin" Cc: Zhang Rui Cc: Josh Boyer Signed-off-by: Andrew Morton Signed-off-by: Thomas Gleixner Signed-off-by: Ingo Molnar --- arch/x86/kernel/rtc.c | 23 ++++++++++++----------- arch/x86/platform/mrst/vrtc.c | 9 +++++++++ 2 files changed, 21 insertions(+), 11 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c index 3f2ad2640d85..ccdbc16b8941 100644 --- a/arch/x86/kernel/rtc.c +++ b/arch/x86/kernel/rtc.c @@ -42,8 +42,11 @@ int mach_set_rtc_mmss(unsigned long nowtime) { int real_seconds, real_minutes, cmos_minutes; unsigned char save_control, save_freq_select; + unsigned long flags; int retval = 0; + spin_lock_irqsave(&rtc_lock, flags); + /* tell the clock it's being set */ save_control = CMOS_READ(RTC_CONTROL); CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); @@ -93,12 +96,17 @@ int mach_set_rtc_mmss(unsigned long nowtime) CMOS_WRITE(save_control, RTC_CONTROL); CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); + spin_unlock_irqrestore(&rtc_lock, flags); + return retval; } unsigned long mach_get_cmos_time(void) { unsigned int status, year, mon, day, hour, min, sec, century = 0; + unsigned long flags; + + spin_lock_irqsave(&rtc_lock, flags); /* * If UIP is clear, then we have >= 244 microseconds before @@ -125,6 +133,8 @@ unsigned long mach_get_cmos_time(void) status = CMOS_READ(RTC_CONTROL); WARN_ON_ONCE(RTC_ALWAYS_BCD && (status & RTC_DM_BINARY)); + spin_unlock_irqrestore(&rtc_lock, flags); + if (RTC_ALWAYS_BCD || !(status & RTC_DM_BINARY)) { sec = bcd2bin(sec); min = bcd2bin(min); @@ -169,24 +179,15 @@ EXPORT_SYMBOL(rtc_cmos_write); int update_persistent_clock(struct timespec now) { - unsigned long flags; - int retval; - - spin_lock_irqsave(&rtc_lock, flags); - retval = x86_platform.set_wallclock(now.tv_sec); - spin_unlock_irqrestore(&rtc_lock, flags); - - return retval; + return x86_platform.set_wallclock(now.tv_sec); } /* not static: needed by APM */ void read_persistent_clock(struct timespec *ts) { - unsigned long retval, flags; + unsigned long retval; - spin_lock_irqsave(&rtc_lock, flags); retval = x86_platform.get_wallclock(); - spin_unlock_irqrestore(&rtc_lock, flags); ts->tv_sec = retval; ts->tv_nsec = 0; diff --git a/arch/x86/platform/mrst/vrtc.c b/arch/x86/platform/mrst/vrtc.c index 73d70d65e76e..6d5dbcdd444a 100644 --- a/arch/x86/platform/mrst/vrtc.c +++ b/arch/x86/platform/mrst/vrtc.c @@ -58,8 +58,11 @@ EXPORT_SYMBOL_GPL(vrtc_cmos_write); unsigned long vrtc_get_time(void) { u8 sec, min, hour, mday, mon; + unsigned long flags; u32 year; + spin_lock_irqsave(&rtc_lock, flags); + while ((vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP)) cpu_relax(); @@ -70,6 +73,8 @@ unsigned long vrtc_get_time(void) mon = vrtc_cmos_read(RTC_MONTH); year = vrtc_cmos_read(RTC_YEAR); + spin_unlock_irqrestore(&rtc_lock, flags); + /* vRTC YEAR reg contains the offset to 1960 */ year += 1960; @@ -83,8 +88,10 @@ unsigned long vrtc_get_time(void) int vrtc_set_mmss(unsigned long nowtime) { int real_sec, real_min; + unsigned long flags; int vrtc_min; + spin_lock_irqsave(&rtc_lock, flags); vrtc_min = vrtc_cmos_read(RTC_MINUTES); real_sec = nowtime % 60; @@ -95,6 +102,8 @@ int vrtc_set_mmss(unsigned long nowtime) vrtc_cmos_write(real_sec, RTC_SECONDS); vrtc_cmos_write(real_min, RTC_MINUTES); + spin_unlock_irqrestore(&rtc_lock, flags); + return 0; } -- cgit v1.2.3 From 153b19a3b9fd8b9478495b9ee1f93f6a77c564f9 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 13 Oct 2011 12:04:20 +0300 Subject: x86, mrst: use a temporary variable for SFI irq SFI tables reside in RAM and should not be modified once they are written. Current code went to set pentry->irq to zero which causes subsequent reads to fail with invalid SFI table checksum. This will break kexec as the second kernel fails to validate SFI tables. To fix this we use temporary variable for irq number. Signed-off-by: Mika Westerberg Reviewed-by: Kirill A. Shutemov Cc: stable@kernel.org Signed-off-by: Linus Torvalds --- arch/x86/platform/mrst/mrst.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index 58425adc22c6..fe73276e026b 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -678,38 +678,40 @@ static int __init sfi_parse_devs(struct sfi_table_header *table) pentry = (struct sfi_device_table_entry *)sb->pentry; for (i = 0; i < num; i++, pentry++) { - if (pentry->irq != (u8)0xff) { /* native RTE case */ + int irq = pentry->irq; + + if (irq != (u8)0xff) { /* native RTE case */ /* these SPI2 devices are not exposed to system as PCI * devices, but they have separate RTE entry in IOAPIC * so we have to enable them one by one here */ - ioapic = mp_find_ioapic(pentry->irq); + ioapic = mp_find_ioapic(irq); irq_attr.ioapic = ioapic; - irq_attr.ioapic_pin = pentry->irq; + irq_attr.ioapic_pin = irq; irq_attr.trigger = 1; irq_attr.polarity = 1; - io_apic_set_pci_routing(NULL, pentry->irq, &irq_attr); + io_apic_set_pci_routing(NULL, irq, &irq_attr); } else - pentry->irq = 0; /* No irq */ + irq = 0; /* No irq */ switch (pentry->type) { case SFI_DEV_TYPE_IPC: /* ID as IRQ is a hack that will go away */ - pdev = platform_device_alloc(pentry->name, pentry->irq); + pdev = platform_device_alloc(pentry->name, irq); if (pdev == NULL) { pr_err("out of memory for SFI platform device '%s'.\n", pentry->name); continue; } - install_irq_resource(pdev, pentry->irq); + install_irq_resource(pdev, irq); pr_debug("info[%2d]: IPC bus, name = %16.16s, " - "irq = 0x%2x\n", i, pentry->name, pentry->irq); + "irq = 0x%2x\n", i, pentry->name, irq); sfi_handle_ipc_dev(pdev); break; case SFI_DEV_TYPE_SPI: memset(&spi_info, 0, sizeof(spi_info)); strncpy(spi_info.modalias, pentry->name, SFI_NAME_LEN); - spi_info.irq = pentry->irq; + spi_info.irq = irq; spi_info.bus_num = pentry->host_num; spi_info.chip_select = pentry->addr; spi_info.max_speed_hz = pentry->max_freq; @@ -726,7 +728,7 @@ static int __init sfi_parse_devs(struct sfi_table_header *table) memset(&i2c_info, 0, sizeof(i2c_info)); bus = pentry->host_num; strncpy(i2c_info.type, pentry->name, SFI_NAME_LEN); - i2c_info.irq = pentry->irq; + i2c_info.irq = irq; i2c_info.addr = pentry->addr; pr_debug("info[%2d]: I2C bus = %d, name = %16.16s, " "irq = 0x%2x, addr = 0x%x\n", i, bus, -- cgit v1.2.3 From 42c2544b2d7d4f287bfa239455af09b6408476e2 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Wed, 7 Sep 2011 16:06:51 +0300 Subject: x86, mrst: Some drivers need to known when an SCU is available Add a notifier so that drivers can hook into SCU availability in order to take actions post initialisation when/if the SCU becomes available. In the ideal world we wouldn't need this and we could avoid any init dependancies of this form, but in practice we can't do it for some cases. Signed-off-by: Alan Cox Signed-off-by: Samuel Ortiz --- arch/x86/include/asm/intel_scu_ipc.h | 22 ++++++++++++++++++++++ arch/x86/platform/mrst/mrst.c | 7 +++++++ 2 files changed, 29 insertions(+) (limited to 'arch/x86/platform') diff --git a/arch/x86/include/asm/intel_scu_ipc.h b/arch/x86/include/asm/intel_scu_ipc.h index 29f66793cc55..4420993acc47 100644 --- a/arch/x86/include/asm/intel_scu_ipc.h +++ b/arch/x86/include/asm/intel_scu_ipc.h @@ -1,6 +1,8 @@ #ifndef _ASM_X86_INTEL_SCU_IPC_H_ #define _ASM_X86_INTEL_SCU_IPC_H_ +#include + #define IPCMSG_VRTC 0xFA /* Set vRTC device */ /* Command id associated with message IPCMSG_VRTC */ @@ -44,4 +46,24 @@ int intel_scu_ipc_i2c_cntrl(u32 addr, u32 *data); /* Update FW version */ int intel_scu_ipc_fw_update(u8 *buffer, u32 length); +extern struct blocking_notifier_head intel_scu_notifier; + +static inline void intel_scu_notifier_add(struct notifier_block *nb) +{ + blocking_notifier_chain_register(&intel_scu_notifier, nb); +} + +static inline void intel_scu_notifier_remove(struct notifier_block *nb) +{ + blocking_notifier_chain_unregister(&intel_scu_notifier, nb); +} + +static inline int intel_scu_notifier_post(unsigned long v, void *p) +{ + return blocking_notifier_call_chain(&intel_scu_notifier, v, p); +} + +#define SCU_AVAILABLE 1 +#define SCU_DOWN 2 + #endif diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index fe73276e026b..51ec016c20d6 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -555,6 +556,9 @@ static void __init intel_scu_i2c_device_register(int bus, i2c_devs[i2c_next_dev++] = new_dev; } +BLOCKING_NOTIFIER_HEAD(intel_scu_notifier); +EXPORT_SYMBOL_GPL(intel_scu_notifier); + /* Called by IPC driver */ void intel_scu_devices_create(void) { @@ -579,6 +583,7 @@ void intel_scu_devices_create(void) } else i2c_register_board_info(i2c_bus[i], i2c_devs[i], 1); } + intel_scu_notifier_post(SCU_AVAILABLE, 0L); } EXPORT_SYMBOL_GPL(intel_scu_devices_create); @@ -587,6 +592,8 @@ void intel_scu_devices_destroy(void) { int i; + intel_scu_notifier_post(SCU_DOWN, 0L); + for (i = 0; i < ipc_next_dev; i++) platform_device_del(ipc_devs[i]); } -- cgit v1.2.3 From 360545c1fcdd458053ede5794c3255c44164cc4a Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Tue, 18 Oct 2011 12:41:22 +0300 Subject: x86, mrst: add platform support for MSIC MFD driver The MSIC MFD driver creates platform devices for MSIC device drivers so we don't need to create them in platform code anymore. This patch adds a new runtime check which determines whether we are running on a Medfield platform and enables the MSIC MFD driver accordingly. Signed-off-by: Mika Westerberg Signed-off-by: Samuel Ortiz --- arch/x86/platform/mrst/mrst.c | 169 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 154 insertions(+), 15 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index 51ec016c20d6..8a7cb3259562 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -481,6 +482,128 @@ static void __init *no_platform_data(void *info) return NULL; } +static struct resource msic_resources[] = { + { + .start = INTEL_MSIC_IRQ_PHYS_BASE, + .end = INTEL_MSIC_IRQ_PHYS_BASE + 64 - 1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct intel_msic_platform_data msic_pdata; + +static struct platform_device msic_device = { + .name = "intel_msic", + .id = -1, + .dev = { + .platform_data = &msic_pdata, + }, + .num_resources = ARRAY_SIZE(msic_resources), + .resource = msic_resources, +}; + +static inline bool mrst_has_msic(void) +{ + return mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL; +} + +static int msic_scu_status_change(struct notifier_block *nb, + unsigned long code, void *data) +{ + if (code == SCU_DOWN) { + platform_device_unregister(&msic_device); + return 0; + } + + return platform_device_register(&msic_device); +} + +static int __init msic_init(void) +{ + static struct notifier_block msic_scu_notifier = { + .notifier_call = msic_scu_status_change, + }; + + /* + * We need to be sure that the SCU IPC is ready before MSIC device + * can be registered. + */ + if (mrst_has_msic()) + intel_scu_notifier_add(&msic_scu_notifier); + + return 0; +} +arch_initcall(msic_init); + +/* + * msic_generic_platform_data - sets generic platform data for the block + * @info: pointer to the SFI device table entry for this block + * @block: MSIC block + * + * Function sets IRQ number from the SFI table entry for given device to + * the MSIC platform data. + */ +static void *msic_generic_platform_data(void *info, enum intel_msic_block block) +{ + struct sfi_device_table_entry *entry = info; + + BUG_ON(block < 0 || block >= INTEL_MSIC_BLOCK_LAST); + msic_pdata.irq[block] = entry->irq; + + return no_platform_data(info); +} + +static void *msic_battery_platform_data(void *info) +{ + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_BATTERY); +} + +static void *msic_gpio_platform_data(void *info) +{ + static struct intel_msic_gpio_pdata pdata; + int gpio = get_gpio_by_name("msic_gpio_base"); + + if (gpio < 0) + return NULL; + + pdata.gpio_base = gpio; + msic_pdata.gpio = &pdata; + + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_GPIO); +} + +static void *msic_audio_platform_data(void *info) +{ + struct platform_device *pdev; + + pdev = platform_device_register_simple("sst-platform", -1, NULL, 0); + if (IS_ERR(pdev)) { + pr_err("failed to create audio platform device\n"); + return NULL; + } + + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_AUDIO); +} + +static void *msic_power_btn_platform_data(void *info) +{ + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_POWER_BTN); +} + +static void *msic_ocd_platform_data(void *info) +{ + static struct intel_msic_ocd_pdata pdata; + int gpio = get_gpio_by_name("ocd_gpio"); + + if (gpio < 0) + return NULL; + + pdata.gpio = gpio; + msic_pdata.ocd = &pdata; + + return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_OCD); +} + static const struct devs_id __initconst device_ids[] = { {"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data}, {"spi_max3111", SFI_DEV_TYPE_SPI, 0, &max3111_platform_data}, @@ -489,7 +612,14 @@ static const struct devs_id __initconst device_ids[] = { {"emc1403", SFI_DEV_TYPE_I2C, 1, &emc1403_platform_data}, {"i2c_accel", SFI_DEV_TYPE_I2C, 0, &lis331dl_platform_data}, {"pmic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data}, - {"msic_audio", SFI_DEV_TYPE_IPC, 1, &no_platform_data}, + + /* MSIC subdevices */ + {"msic_battery", SFI_DEV_TYPE_IPC, 1, &msic_battery_platform_data}, + {"msic_gpio", SFI_DEV_TYPE_IPC, 1, &msic_gpio_platform_data}, + {"msic_audio", SFI_DEV_TYPE_IPC, 1, &msic_audio_platform_data}, + {"msic_power_btn", SFI_DEV_TYPE_IPC, 1, &msic_power_btn_platform_data}, + {"msic_ocd", SFI_DEV_TYPE_IPC, 1, &msic_ocd_platform_data}, + {}, }; @@ -610,19 +740,37 @@ static void __init install_irq_resource(struct platform_device *pdev, int irq) platform_device_add_resources(pdev, &res, 1); } -static void __init sfi_handle_ipc_dev(struct platform_device *pdev) +static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *entry) { const struct devs_id *dev = device_ids; + struct platform_device *pdev; void *pdata = NULL; while (dev->name[0]) { if (dev->type == SFI_DEV_TYPE_IPC && - !strncmp(dev->name, pdev->name, SFI_NAME_LEN)) { - pdata = dev->get_platform_data(pdev); + !strncmp(dev->name, entry->name, SFI_NAME_LEN)) { + pdata = dev->get_platform_data(entry); break; } dev++; } + + /* + * On Medfield the platform device creation is handled by the MSIC + * MFD driver so we don't need to do it here. + */ + if (mrst_has_msic()) + return; + + /* ID as IRQ is a hack that will go away */ + pdev = platform_device_alloc(entry->name, entry->irq); + if (pdev == NULL) { + pr_err("out of memory for SFI platform device '%s'.\n", + entry->name); + return; + } + install_irq_resource(pdev, entry->irq); + pdev->dev.platform_data = pdata; intel_scu_device_register(pdev); } @@ -675,7 +823,6 @@ static int __init sfi_parse_devs(struct sfi_table_header *table) struct sfi_device_table_entry *pentry; struct spi_board_info spi_info; struct i2c_board_info i2c_info; - struct platform_device *pdev; int num, i, bus; int ioapic; struct io_apic_irq_attr irq_attr; @@ -703,17 +850,9 @@ static int __init sfi_parse_devs(struct sfi_table_header *table) switch (pentry->type) { case SFI_DEV_TYPE_IPC: - /* ID as IRQ is a hack that will go away */ - pdev = platform_device_alloc(pentry->name, irq); - if (pdev == NULL) { - pr_err("out of memory for SFI platform device '%s'.\n", - pentry->name); - continue; - } - install_irq_resource(pdev, irq); pr_debug("info[%2d]: IPC bus, name = %16.16s, " - "irq = 0x%2x\n", i, pentry->name, irq); - sfi_handle_ipc_dev(pdev); + "irq = 0x%2x\n", i, pentry->name, pentry->irq); + sfi_handle_ipc_dev(pentry); break; case SFI_DEV_TYPE_SPI: memset(&spi_info, 0, sizeof(spi_info)); -- cgit v1.2.3 From 69c60c88eeb364ebf58432f9bc38033522d58767 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Thu, 26 May 2011 12:22:53 -0400 Subject: x86: Fix files explicitly requiring export.h for EXPORT_SYMBOL/THIS_MODULE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These files were implicitly getting EXPORT_SYMBOL via device.h which was including module.h, but that will be fixed up shortly. By fixing these now, we can avoid seeing things like: arch/x86/kernel/rtc.c:29: warning: type defaults to ‘int’ in declaration of ‘EXPORT_SYMBOL’ arch/x86/kernel/pci-dma.c:20: warning: type defaults to ‘int’ in declaration of ‘EXPORT_SYMBOL’ arch/x86/kernel/e820.c:69: warning: type defaults to ‘int’ in declaration of ‘EXPORT_SYMBOL_GPL’ [ with input from Randy Dunlap and also from Stephen Rothwell ] Signed-off-by: Paul Gortmaker --- arch/x86/kernel/cpu/amd.c | 1 + arch/x86/kernel/cpu/mcheck/mce-apei.c | 1 + arch/x86/kernel/cpu/mcheck/mce.c | 1 + arch/x86/kernel/cpu/mcheck/therm_throt.c | 1 + arch/x86/kernel/cpu/perf_event_intel.c | 1 + arch/x86/kernel/devicetree.c | 1 + arch/x86/kernel/e820.c | 1 + arch/x86/kernel/hpet.c | 1 + arch/x86/kernel/irq.c | 1 + arch/x86/kernel/nmi.c | 1 + arch/x86/kernel/pci-dma.c | 1 + arch/x86/kernel/probe_roms.c | 4 ++-- arch/x86/kernel/rtc.c | 1 + arch/x86/kernel/smp.c | 1 + arch/x86/kernel/tboot.c | 1 + arch/x86/kernel/time.c | 1 + arch/x86/kernel/topology.c | 1 + arch/x86/pci/i386.c | 1 + arch/x86/pci/legacy.c | 1 + arch/x86/platform/efi/efi.c | 1 + arch/x86/platform/mrst/vrtc.c | 1 + arch/x86/platform/olpc/olpc-xo1-pm.c | 1 + arch/x86/platform/uv/bios_uv.c | 1 + arch/x86/power/cpu.c | 1 + 24 files changed, 25 insertions(+), 2 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c index 46ae4f65fc7f..c7e46cb35327 100644 --- a/arch/x86/kernel/cpu/amd.c +++ b/arch/x86/kernel/cpu/amd.c @@ -1,3 +1,4 @@ +#include #include #include #include diff --git a/arch/x86/kernel/cpu/mcheck/mce-apei.c b/arch/x86/kernel/cpu/mcheck/mce-apei.c index 83930deec3c6..507ea58688e2 100644 --- a/arch/x86/kernel/cpu/mcheck/mce-apei.c +++ b/arch/x86/kernel/cpu/mcheck/mce-apei.c @@ -28,6 +28,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include #include #include diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c index 7b5063a6ad42..537c89e00095 100644 --- a/arch/x86/kernel/cpu/mcheck/mce.c +++ b/arch/x86/kernel/cpu/mcheck/mce.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include diff --git a/arch/x86/kernel/cpu/mcheck/therm_throt.c b/arch/x86/kernel/cpu/mcheck/therm_throt.c index 27c625178bf1..787e06c84ea6 100644 --- a/arch/x86/kernel/cpu/mcheck/therm_throt.c +++ b/arch/x86/kernel/cpu/mcheck/therm_throt.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index e09ca20e86ee..2be5ebe99872 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c index a621f3427685..52821799a702 100644 --- a/arch/x86/kernel/devicetree.c +++ b/arch/x86/kernel/devicetree.c @@ -2,6 +2,7 @@ * Architecture specific OF callbacks. */ #include +#include #include #include #include diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index 3e2ef8425316..303a0e48f076 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/kernel/hpet.c b/arch/x86/kernel/hpet.c index 4aecc54236a9..b946a9eac7d9 100644 --- a/arch/x86/kernel/hpet.c +++ b/arch/x86/kernel/hpet.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c index 6c0802eb2f7f..429e0c92924e 100644 --- a/arch/x86/kernel/irq.c +++ b/arch/x86/kernel/irq.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c index 7ec5bd140b87..b9c8628974af 100644 --- a/arch/x86/kernel/nmi.c +++ b/arch/x86/kernel/nmi.c @@ -17,6 +17,7 @@ #include #include #include +#include #include diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c index 622872054fbe..80dc793b3f63 100644 --- a/arch/x86/kernel/pci-dma.c +++ b/arch/x86/kernel/pci-dma.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/kernel/probe_roms.c b/arch/x86/kernel/probe_roms.c index 63228035f9d7..34e06e84ce31 100644 --- a/arch/x86/kernel/probe_roms.c +++ b/arch/x86/kernel/probe_roms.c @@ -10,9 +10,9 @@ #include #include #include -#include - +#include +#include #include #include #include diff --git a/arch/x86/kernel/rtc.c b/arch/x86/kernel/rtc.c index ccdbc16b8941..348ce016a835 100644 --- a/arch/x86/kernel/rtc.c +++ b/arch/x86/kernel/rtc.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include diff --git a/arch/x86/kernel/smp.c b/arch/x86/kernel/smp.c index 013e7eba83bb..16204dc15484 100644 --- a/arch/x86/kernel/smp.c +++ b/arch/x86/kernel/smp.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c index e07a2fc876b9..e2410e27f97e 100644 --- a/arch/x86/kernel/tboot.c +++ b/arch/x86/kernel/tboot.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/kernel/time.c b/arch/x86/kernel/time.c index 5a64d057be57..dd5fbf4101fc 100644 --- a/arch/x86/kernel/time.c +++ b/arch/x86/kernel/time.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include diff --git a/arch/x86/kernel/topology.c b/arch/x86/kernel/topology.c index 8927486a4649..76ee97709a00 100644 --- a/arch/x86/kernel/topology.c +++ b/arch/x86/kernel/topology.c @@ -26,6 +26,7 @@ * Send feedback to */ #include +#include #include #include #include diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index 494f2e7ea2b4..794b092d01ae 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -26,6 +26,7 @@ #include #include +#include #include #include #include diff --git a/arch/x86/pci/legacy.c b/arch/x86/pci/legacy.c index c89266be6048..2c2aeabc2609 100644 --- a/arch/x86/pci/legacy.c +++ b/arch/x86/pci/legacy.c @@ -2,6 +2,7 @@ * legacy.c - traditional, old school PCI bus probing */ #include +#include #include #include diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index 3ae4128013e6..37718f0f053d 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/x86/platform/mrst/vrtc.c b/arch/x86/platform/mrst/vrtc.c index 6d5dbcdd444a..a8ac6f1eb66d 100644 --- a/arch/x86/platform/mrst/vrtc.c +++ b/arch/x86/platform/mrst/vrtc.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include diff --git a/arch/x86/platform/olpc/olpc-xo1-pm.c b/arch/x86/platform/olpc/olpc-xo1-pm.c index 6f3855a5a2f7..0ce8616c88ae 100644 --- a/arch/x86/platform/olpc/olpc-xo1-pm.c +++ b/arch/x86/platform/olpc/olpc-xo1-pm.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include diff --git a/arch/x86/platform/uv/bios_uv.c b/arch/x86/platform/uv/bios_uv.c index 8bc57baaa9ad..766612137a62 100644 --- a/arch/x86/platform/uv/bios_uv.c +++ b/arch/x86/platform/uv/bios_uv.c @@ -20,6 +20,7 @@ */ #include +#include #include #include #include diff --git a/arch/x86/power/cpu.c b/arch/x86/power/cpu.c index 87bb35e34ef1..f10c0afa1cb4 100644 --- a/arch/x86/power/cpu.c +++ b/arch/x86/power/cpu.c @@ -9,6 +9,7 @@ */ #include +#include #include #include -- cgit v1.2.3 From 783ac47c2aad07ff68f9432968d2f0829ef81543 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Fri, 27 May 2011 14:46:07 -0400 Subject: x86: efi_32.c is implicitly getting asm/desc.h via module.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to clean up the chain of includes stumbling through module.h, and when we do that, we'll see: CC arch/x86/platform/efi/efi_32.o efi/efi_32.c: In function ‘efi_call_phys_prelog’: efi/efi_32.c:80: error: implicit declaration of function ‘get_cpu_gdt_table’ efi/efi_32.c:82: error: implicit declaration of function ‘load_gdt’ make[4]: *** [arch/x86/platform/efi/efi_32.o] Error 1 Include asm/desc.h so that there are no implicit include assumptions. Signed-off-by: Paul Gortmaker --- arch/x86/platform/efi/efi_32.c | 1 + 1 file changed, 1 insertion(+) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/efi/efi_32.c b/arch/x86/platform/efi/efi_32.c index 5cab48ee61a4..e36bf714cb77 100644 --- a/arch/x86/platform/efi/efi_32.c +++ b/arch/x86/platform/efi/efi_32.c @@ -25,6 +25,7 @@ #include #include +#include #include #include #include -- cgit v1.2.3 From 22f4521d664030e417f41953e922f61c65f2e189 Mon Sep 17 00:00:00 2001 From: Len Brown Date: Fri, 12 Aug 2011 00:13:47 -0400 Subject: mrst pmu: update comment referenced MeeGo, in particular, but really means Linux, in general. Signed-off-by: Len Brown --- arch/x86/platform/mrst/pmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/mrst/pmu.c b/arch/x86/platform/mrst/pmu.c index 9281da7d91bd..c0ac06da57ac 100644 --- a/arch/x86/platform/mrst/pmu.c +++ b/arch/x86/platform/mrst/pmu.c @@ -70,7 +70,7 @@ static struct mrst_device mrst_devs[] = { /* 24 */ { 0x4110, 0 }, /* Lincroft */ }; -/* n.b. We ignore PCI-id 0x815 in LSS9 b/c MeeGo has no driver for it */ +/* n.b. We ignore PCI-id 0x815 in LSS9 b/c Linux has no driver for it */ static u16 mrst_lss9_pci_ids[] = {0x080a, 0x0814, 0}; static u16 mrst_lss10_pci_ids[] = {0x0800, 0x0801, 0x0802, 0x0803, 0x0804, 0x0805, 0x080f, 0}; -- cgit v1.2.3 From 0a9153261d54c432bc0bdc88607f24c835ac729c Mon Sep 17 00:00:00 2001 From: Dirk Brandewie Date: Thu, 10 Nov 2011 13:42:53 +0000 Subject: x86/mrst: Add support for Penwell clock calibration Signed-off-by: Dirk Brandewie Signed-off-by: Alan Cox Signed-off-by: Ingo Molnar --- arch/x86/include/asm/mrst.h | 7 +++++++ arch/x86/platform/mrst/mrst.c | 33 ++++++++++++++++++++++++++++----- 2 files changed, 35 insertions(+), 5 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/include/asm/mrst.h b/arch/x86/include/asm/mrst.h index 719f00b28ff5..e6283129c821 100644 --- a/arch/x86/include/asm/mrst.h +++ b/arch/x86/include/asm/mrst.h @@ -44,6 +44,13 @@ enum mrst_timer_options { extern enum mrst_timer_options mrst_timer_options; +/* + * Penwell uses spread spectrum clock, so the freq number is not exactly + * the same as reported by MSR based on SDM. + */ +#define PENWELL_FSB_FREQ_83SKU 83200 +#define PENWELL_FSB_FREQ_100SKU 99840 + #define SFI_MTMR_MAX_NUM 8 #define SFI_MRTC_MAX 8 diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index 6ed7afdaf4af..b7f14e5b2c66 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -187,11 +187,34 @@ int __init sfi_parse_mrtc(struct sfi_table_header *table) static unsigned long __init mrst_calibrate_tsc(void) { unsigned long flags, fast_calibrate; - - local_irq_save(flags); - fast_calibrate = apbt_quick_calibrate(); - local_irq_restore(flags); - + if (__mrst_cpu_chip == MRST_CPU_CHIP_PENWELL) { + u32 lo, hi, ratio, fsb; + + rdmsr(MSR_IA32_PERF_STATUS, lo, hi); + pr_debug("IA32 perf status is 0x%x, 0x%0x\n", lo, hi); + ratio = (hi >> 8) & 0x1f; + pr_debug("ratio is %d\n", ratio); + if (!ratio) { + pr_err("read a zero ratio, should be incorrect!\n"); + pr_err("force tsc ratio to 16 ...\n"); + ratio = 16; + } + rdmsr(MSR_FSB_FREQ, lo, hi); + if ((lo & 0x7) == 0x7) + fsb = PENWELL_FSB_FREQ_83SKU; + else + fsb = PENWELL_FSB_FREQ_100SKU; + fast_calibrate = ratio * fsb; + pr_debug("read penwell tsc %lu khz\n", fast_calibrate); + lapic_timer_frequency = fsb * 1000 / HZ; + /* mark tsc clocksource as reliable */ + set_cpu_cap(&boot_cpu_data, X86_FEATURE_TSC_RELIABLE); + } else { + local_irq_save(flags); + fast_calibrate = apbt_quick_calibrate(); + local_irq_restore(flags); + } + if (fast_calibrate) return fast_calibrate; -- cgit v1.2.3 From 064a59b6dd1f341cc478c212bb436e3da9cb8d04 Mon Sep 17 00:00:00 2001 From: Jacob Pan Date: Thu, 10 Nov 2011 13:43:05 +0000 Subject: x86/mrst: Avoid reporting wrong nmi status Moorestown/Medfield platform does not have port 0x61 to report NMI status, nor does it have external NMI sources. The only NMI sources are from lapic, as results of perf counter overflow or IPI, e.g. NMI watchdog or spin lock debug. Reading port 0x61 on Moorestown will return 0xff which misled NMI handlers to false critical errors such memory parity error. The subsequent ioport access for NMI handling can also cause undefined behavior on Moorestown. This patch allows kernel process NMI due to watchdog or backrace dump without unnecessary hangs. Signed-off-by: Jacob Pan Signed-off-by: Ingo Molnar [hand applied] Signed-off-by: Alan Cox --- arch/x86/include/asm/mach_traps.h | 2 +- arch/x86/include/asm/x86_init.h | 1 + arch/x86/kernel/nmi.c | 2 +- arch/x86/kernel/x86_init.c | 2 ++ arch/x86/platform/mrst/mrst.c | 13 +++++++++++++ 5 files changed, 18 insertions(+), 2 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/include/asm/mach_traps.h b/arch/x86/include/asm/mach_traps.h index 72a8b52e7dfd..a01e7ec7d237 100644 --- a/arch/x86/include/asm/mach_traps.h +++ b/arch/x86/include/asm/mach_traps.h @@ -17,7 +17,7 @@ #define NMI_REASON_CLEAR_IOCHK 0x08 #define NMI_REASON_CLEAR_MASK 0x0f -static inline unsigned char get_nmi_reason(void) +static inline unsigned char default_get_nmi_reason(void) { return inb(NMI_REASON_PORT); } diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h index f864fbe474c6..1971e652d24b 100644 --- a/arch/x86/include/asm/x86_init.h +++ b/arch/x86/include/asm/x86_init.h @@ -167,6 +167,7 @@ struct x86_platform_ops { void (*iommu_shutdown)(void); bool (*is_untracked_pat_range)(u64 start, u64 end); void (*nmi_init)(void); + unsigned char (*get_nmi_reason)(void); int (*i8042_detect)(void); }; diff --git a/arch/x86/kernel/nmi.c b/arch/x86/kernel/nmi.c index b9c8628974af..27d1e7cbdb6c 100644 --- a/arch/x86/kernel/nmi.c +++ b/arch/x86/kernel/nmi.c @@ -348,7 +348,7 @@ static notrace __kprobes void default_do_nmi(struct pt_regs *regs) /* Non-CPU-specific NMI: NMI sources can be processed on any CPU */ raw_spin_lock(&nmi_reason_lock); - reason = get_nmi_reason(); + reason = x86_platform.get_nmi_reason(); if (reason & NMI_REASON_MASK) { if (reason & NMI_REASON_SERR) diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c index 701c7be442f1..c1d6cd549397 100644 --- a/arch/x86/kernel/x86_init.c +++ b/arch/x86/kernel/x86_init.c @@ -21,6 +21,7 @@ #include #include #include +#include void __cpuinit x86_init_noop(void) { } void __init x86_init_uint_noop(unsigned int unused) { } @@ -104,6 +105,7 @@ struct x86_platform_ops x86_platform = { .iommu_shutdown = iommu_shutdown_noop, .is_untracked_pat_range = is_ISA_range, .nmi_init = default_nmi_init, + .get_nmi_reason = default_get_nmi_reason, .i8042_detect = default_i8042_detect }; diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index b7f14e5b2c66..9b9ee292c107 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -276,6 +276,17 @@ static void mrst_reboot(void) intel_scu_ipc_simple_command(0xf1, 0); } +/* + * Moorestown does not have external NMI source nor port 0x61 to report + * NMI status. The possible NMI sources are from pmu as a result of NMI + * watchdog or lock debug. Reading io port 0x61 results in 0xff which + * misled NMI handler. + */ +static unsigned char mrst_get_nmi_reason(void) +{ + return 0; +} + /* * Moorestown specific x86_init function overrides and early setup * calls. @@ -297,6 +308,8 @@ void __init x86_mrst_early_setup(void) x86_platform.calibrate_tsc = mrst_calibrate_tsc; x86_platform.i8042_detect = mrst_i8042_detect; x86_init.timers.wallclock_init = mrst_rtc_init; + x86_platform.get_nmi_reason = mrst_get_nmi_reason; + x86_init.pci.init = pci_mrst_init; x86_init.pci.fixup_irqs = x86_init_noop; -- cgit v1.2.3 From f2ee442115c9b6219083c019939a9cc0c9abb2f8 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Thu, 10 Nov 2011 13:21:10 +0000 Subject: ce4100: fix a build error Fix a build error. CE4100 with no serial errors because the alternate function is only a prototype not a null function as intended. Signed-off-by: Zhang Rui Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- arch/x86/platform/ce4100/ce4100.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/ce4100/ce4100.c b/arch/x86/platform/ce4100/ce4100.c index 28071bb31db7..4c61b52191eb 100644 --- a/arch/x86/platform/ce4100/ce4100.c +++ b/arch/x86/platform/ce4100/ce4100.c @@ -109,7 +109,7 @@ static __init void sdv_serial_fixup(void) } #else -static inline void sdv_serial_fixup(void); +static inline void sdv_serial_fixup(void) {}; #endif static void __init sdv_arch_setup(void) -- cgit v1.2.3 From 57e6319dd61d5ca10fe8dd57bcce8c0e2c480799 Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Thu, 10 Nov 2011 13:23:39 +0000 Subject: vrtc: change its year offset from 1960 to 1972 Real world year equals the value in vrtc YEAR register plus an offset. We used 1960 as the offset to make leap year consistent, but for a device's first use, its YEAR register is 0 and the system year will be parsed as 1960 which is not a valid UNIX time and will cause many applications to fail mysteriously. So we use 1972 instead to fix this issue. Updated patch which adds a sanity check suggested by Mathias This isn't a change in behaviour for systems, because 1972 is the one we actually use. It's the old version in upstream which is out of sync with all devices. Signed-off-by: Feng Tang Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- arch/x86/platform/mrst/vrtc.c | 4 ++-- drivers/rtc/rtc-mrst.c | 19 +++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/mrst/vrtc.c b/arch/x86/platform/mrst/vrtc.c index a8ac6f1eb66d..225bd0f0f675 100644 --- a/arch/x86/platform/mrst/vrtc.c +++ b/arch/x86/platform/mrst/vrtc.c @@ -76,8 +76,8 @@ unsigned long vrtc_get_time(void) spin_unlock_irqrestore(&rtc_lock, flags); - /* vRTC YEAR reg contains the offset to 1960 */ - year += 1960; + /* vRTC YEAR reg contains the offset to 1972 */ + year += 1972; printk(KERN_INFO "vRTC: sec: %d min: %d hour: %d day: %d " "mon: %d year: %d\n", sec, min, hour, mday, mon, year); diff --git a/drivers/rtc/rtc-mrst.c b/drivers/rtc/rtc-mrst.c index d33544802a2e..bb21f443fb70 100644 --- a/drivers/rtc/rtc-mrst.c +++ b/drivers/rtc/rtc-mrst.c @@ -76,12 +76,15 @@ static inline unsigned char vrtc_is_updating(void) /* * rtc_time's year contains the increment over 1900, but vRTC's YEAR * register can't be programmed to value larger than 0x64, so vRTC - * driver chose to use 1960 (1970 is UNIX time start point) as the base, + * driver chose to use 1972 (1970 is UNIX time start point) as the base, * and does the translation at read/write time. * - * Why not just use 1970 as the offset? it's because using 1960 will + * Why not just use 1970 as the offset? it's because using 1972 will * make it consistent in leap year setting for both vrtc and low-level - * physical rtc devices. + * physical rtc devices. Then why not use 1960 as the offset? If we use + * 1960, for a device's first use, its YEAR register is 0 and the system + * year will be parsed as 1960 which is not a valid UNIX time and will + * cause many applications to fail mysteriously. */ static int mrst_read_time(struct device *dev, struct rtc_time *time) { @@ -99,10 +102,10 @@ static int mrst_read_time(struct device *dev, struct rtc_time *time) time->tm_year = vrtc_cmos_read(RTC_YEAR); spin_unlock_irqrestore(&rtc_lock, flags); - /* Adjust for the 1960/1900 */ - time->tm_year += 60; + /* Adjust for the 1972/1900 */ + time->tm_year += 72; time->tm_mon--; - return RTC_24H; + return rtc_valid_tm(time); } static int mrst_set_time(struct device *dev, struct rtc_time *time) @@ -119,9 +122,9 @@ static int mrst_set_time(struct device *dev, struct rtc_time *time) min = time->tm_min; sec = time->tm_sec; - if (yrs < 70 || yrs > 138) + if (yrs < 72 || yrs > 138) return -EINVAL; - yrs -= 60; + yrs -= 72; spin_lock_irqsave(&rtc_lock, flags); -- cgit v1.2.3 From 9f80d8b68fb244bbd69c55aced663b91098544fc Mon Sep 17 00:00:00 2001 From: William Douglas Date: Thu, 10 Nov 2011 13:50:38 +0000 Subject: bma023: Add SFI translation for this device This needed the sfi IRQ 0xFF fix to go in first. It simply plumbs in the bma023 driver with the firmware naming of it. Signed-off-by: William Douglas Signed-off-by: Alan Cox Signed-off-by: Linus Torvalds --- arch/x86/platform/mrst/mrst.c | 1 + 1 file changed, 1 insertion(+) (limited to 'arch/x86/platform') diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c index 6ed7afdaf4af..541020df0da6 100644 --- a/arch/x86/platform/mrst/mrst.c +++ b/arch/x86/platform/mrst/mrst.c @@ -608,6 +608,7 @@ static void *msic_ocd_platform_data(void *info) } static const struct devs_id __initconst device_ids[] = { + {"bma023", SFI_DEV_TYPE_I2C, 1, &no_platform_data}, {"pmic_gpio", SFI_DEV_TYPE_SPI, 1, &pmic_gpio_platform_data}, {"spi_max3111", SFI_DEV_TYPE_SPI, 0, &max3111_platform_data}, {"i2c_max7315", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, -- cgit v1.2.3