diff options
Diffstat (limited to 'arch/x86/platform')
55 files changed, 4310 insertions, 2104 deletions
diff --git a/arch/x86/platform/Makefile b/arch/x86/platform/Makefile index 01e0231a113..85afde1fa3e 100644 --- a/arch/x86/platform/Makefile +++ b/arch/x86/platform/Makefile @@ -4,10 +4,9 @@ obj-y	+= efi/  obj-y	+= geode/  obj-y	+= goldfish/  obj-y	+= iris/ -obj-y	+= mrst/ +obj-y	+= intel-mid/  obj-y	+= olpc/  obj-y	+= scx200/  obj-y	+= sfi/  obj-y	+= ts5500/ -obj-y	+= visws/  obj-y	+= uv/ diff --git a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile index 6db1cc4c753..d51045afcaa 100644 --- a/arch/x86/platform/efi/Makefile +++ b/arch/x86/platform/efi/Makefile @@ -1,2 +1,4 @@  obj-$(CONFIG_EFI) 		+= efi.o efi_$(BITS).o efi_stub_$(BITS).o  obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o +obj-$(CONFIG_EARLY_PRINTK_EFI)	+= early_printk.o +obj-$(CONFIG_EFI_MIXED)		+= efi_thunk_$(BITS).o diff --git a/arch/x86/platform/efi/early_printk.c b/arch/x86/platform/efi/early_printk.c new file mode 100644 index 00000000000..52414211729 --- /dev/null +++ b/arch/x86/platform/efi/early_printk.c @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2013 Intel Corporation; author Matt Fleming + * + *  This file is part of the Linux kernel, and is made available under + *  the terms of the GNU General Public License version 2. + */ + +#include <linux/console.h> +#include <linux/efi.h> +#include <linux/font.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <asm/setup.h> + +static const struct font_desc *font; +static u32 efi_x, efi_y; +static void *efi_fb; +static bool early_efi_keep; + +/* + * efi earlyprintk need use early_ioremap to map the framebuffer. + * But early_ioremap is not usable for earlyprintk=efi,keep, ioremap should + * be used instead. ioremap will be available after paging_init() which is + * earlier than initcall callbacks. Thus adding this early initcall function + * early_efi_map_fb to map the whole efi framebuffer. + */ +static __init int early_efi_map_fb(void) +{ +	unsigned long base, size; + +	if (!early_efi_keep) +		return 0; + +	base = boot_params.screen_info.lfb_base; +	size = boot_params.screen_info.lfb_size; +	efi_fb = ioremap(base, size); + +	return efi_fb ? 0 : -ENOMEM; +} +early_initcall(early_efi_map_fb); + +/* + * early_efi_map maps efi framebuffer region [start, start + len -1] + * In case earlyprintk=efi,keep we have the whole framebuffer mapped already + * so just return the offset efi_fb + start. + */ +static __init_refok void *early_efi_map(unsigned long start, unsigned long len) +{ +	unsigned long base; + +	base = boot_params.screen_info.lfb_base; + +	if (efi_fb) +		return (efi_fb + start); +	else +		return early_ioremap(base + start, len); +} + +static __init_refok void early_efi_unmap(void *addr, unsigned long len) +{ +	if (!efi_fb) +		early_iounmap(addr, len); +} + +static void early_efi_clear_scanline(unsigned int y) +{ +	unsigned long *dst; +	u16 len; + +	len = boot_params.screen_info.lfb_linelength; +	dst = early_efi_map(y*len, len); +	if (!dst) +		return; + +	memset(dst, 0, len); +	early_efi_unmap(dst, len); +} + +static void early_efi_scroll_up(void) +{ +	unsigned long *dst, *src; +	u16 len; +	u32 i, height; + +	len = boot_params.screen_info.lfb_linelength; +	height = boot_params.screen_info.lfb_height; + +	for (i = 0; i < height - font->height; i++) { +		dst = early_efi_map(i*len, len); +		if (!dst) +			return; + +		src = early_efi_map((i + font->height) * len, len); +		if (!src) { +			early_efi_unmap(dst, len); +			return; +		} + +		memmove(dst, src, len); + +		early_efi_unmap(src, len); +		early_efi_unmap(dst, len); +	} +} + +static void early_efi_write_char(u32 *dst, unsigned char c, unsigned int h) +{ +	const u32 color_black = 0x00000000; +	const u32 color_white = 0x00ffffff; +	const u8 *src; +	u8 s8; +	int m; + +	src = font->data + c * font->height; +	s8 = *(src + h); + +	for (m = 0; m < 8; m++) { +		if ((s8 >> (7 - m)) & 1) +			*dst = color_white; +		else +			*dst = color_black; +		dst++; +	} +} + +static void +early_efi_write(struct console *con, const char *str, unsigned int num) +{ +	struct screen_info *si; +	unsigned int len; +	const char *s; +	void *dst; + +	si = &boot_params.screen_info; +	len = si->lfb_linelength; + +	while (num) { +		unsigned int linemax; +		unsigned int h, count = 0; + +		for (s = str; *s && *s != '\n'; s++) { +			if (count == num) +				break; +			count++; +		} + +		linemax = (si->lfb_width - efi_x) / font->width; +		if (count > linemax) +			count = linemax; + +		for (h = 0; h < font->height; h++) { +			unsigned int n, x; + +			dst = early_efi_map((efi_y + h) * len, len); +			if (!dst) +				return; + +			s = str; +			n = count; +			x = efi_x; + +			while (n-- > 0) { +				early_efi_write_char(dst + x*4, *s, h); +				x += font->width; +				s++; +			} + +			early_efi_unmap(dst, len); +		} + +		num -= count; +		efi_x += count * font->width; +		str += count; + +		if (num > 0 && *s == '\n') { +			efi_x = 0; +			efi_y += font->height; +			str++; +			num--; +		} + +		if (efi_x >= si->lfb_width) { +			efi_x = 0; +			efi_y += font->height; +		} + +		if (efi_y + font->height > si->lfb_height) { +			u32 i; + +			efi_y -= font->height; +			early_efi_scroll_up(); + +			for (i = 0; i < font->height; i++) +				early_efi_clear_scanline(efi_y + i); +		} +	} +} + +static __init int early_efi_setup(struct console *con, char *options) +{ +	struct screen_info *si; +	u16 xres, yres; +	u32 i; + +	si = &boot_params.screen_info; +	xres = si->lfb_width; +	yres = si->lfb_height; + +	/* +	 * early_efi_write_char() implicitly assumes a framebuffer with +	 * 32-bits per pixel. +	 */ +	if (si->lfb_depth != 32) +		return -ENODEV; + +	font = get_default_font(xres, yres, -1, -1); +	if (!font) +		return -ENODEV; + +	efi_y = rounddown(yres, font->height) - font->height; +	for (i = 0; i < (yres - efi_y) / font->height; i++) +		early_efi_scroll_up(); + +	/* early_console_register will unset CON_BOOT in case ,keep */ +	if (!(con->flags & CON_BOOT)) +		early_efi_keep = true; +	return 0; +} + +struct console early_efi_console = { +	.name =		"earlyefi", +	.write =	early_efi_write, +	.setup =	early_efi_setup, +	.flags =	CON_PRINTBUFFER, +	.index =	-1, +}; diff --git a/arch/x86/platform/efi/efi-bgrt.c b/arch/x86/platform/efi/efi-bgrt.c index 7145ec63c52..f15103dff4b 100644 --- a/arch/x86/platform/efi/efi-bgrt.c +++ b/arch/x86/platform/efi/efi-bgrt.c @@ -42,14 +42,15 @@ void __init efi_bgrt_init(void)  	if (bgrt_tab->header.length < sizeof(*bgrt_tab))  		return; -	if (bgrt_tab->version != 1) +	if (bgrt_tab->version != 1 || bgrt_tab->status != 1)  		return;  	if (bgrt_tab->image_type != 0 || !bgrt_tab->image_address)  		return;  	image = efi_lookup_mapped_addr(bgrt_tab->image_address);  	if (!image) { -		image = ioremap(bgrt_tab->image_address, sizeof(bmp_header)); +		image = early_memremap(bgrt_tab->image_address, +				       sizeof(bmp_header));  		ioremapped = true;  		if (!image)  			return; @@ -57,7 +58,7 @@ void __init efi_bgrt_init(void)  	memcpy_fromio(&bmp_header, image, sizeof(bmp_header));  	if (ioremapped) -		iounmap(image); +		early_iounmap(image, sizeof(bmp_header));  	bgrt_image_size = bmp_header.size;  	bgrt_image = kmalloc(bgrt_image_size, GFP_KERNEL); @@ -65,7 +66,8 @@ void __init efi_bgrt_init(void)  		return;  	if (ioremapped) { -		image = ioremap(bgrt_tab->image_address, bmp_header.size); +		image = early_memremap(bgrt_tab->image_address, +				       bmp_header.size);  		if (!image) {  			kfree(bgrt_image);  			bgrt_image = NULL; @@ -75,5 +77,5 @@ void __init efi_bgrt_init(void)  	memcpy_fromio(bgrt_image, image, bgrt_image_size);  	if (ioremapped) -		iounmap(image); +		early_iounmap(image, bmp_header.size);  } diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c index c7e22ab29a5..87fc96bcc13 100644 --- a/arch/x86/platform/efi/efi.c +++ b/arch/x86/platform/efi/efi.c @@ -12,6 +12,8 @@   *	Bibo Mao <bibo.mao@intel.com>   *	Chandramouli Narayanan <mouli@linux.intel.com>   *	Huang Ying <ying.huang@intel.com> + * Copyright (C) 2013 SuSE Labs + *	Borislav Petkov <bp@suse.de> - runtime services VA mapping   *   * Copied from efi_32.c to eliminate the duplicated code between EFI   * 32/64 support code. --ying 2007-10-26 @@ -50,8 +52,9 @@  #include <asm/tlbflush.h>  #include <asm/x86_init.h>  #include <asm/rtc.h> +#include <asm/uv/uv.h> -#define EFI_DEBUG	1 +#define EFI_DEBUG  #define EFI_MIN_RESERVE 5120 @@ -60,36 +63,21 @@  static efi_char16_t efi_dummy_name[6] = { 'D', 'U', 'M', 'M', 'Y', 0 }; -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;  static struct efi efi_phys __initdata;  static efi_system_table_t efi_systab __initdata; -unsigned long x86_efi_facility; +static efi_config_table_type_t arch_tables[] __initdata = { +#ifdef CONFIG_X86_UV +	{UV_SYSTEM_TABLE_GUID, "UVsystab", &efi.uv_systab}, +#endif +	{NULL_GUID, NULL, NULL}, +}; -/* - * Returns 1 if 'facility' is enabled, 0 otherwise. - */ -int efi_enabled(int facility) -{ -	return test_bit(facility, &x86_efi_facility) != 0; -} -EXPORT_SYMBOL(efi_enabled); +u64 efi_setup;		/* efi setup_data physical address */ -static bool __initdata disable_runtime = false; +static bool disable_runtime __initdata = false;  static int __init setup_noefi(char *arg)  {  	disable_runtime = true; @@ -116,14 +104,13 @@ static int __init setup_storage_paranoia(char *arg)  }  early_param("efi_no_storage_paranoia", setup_storage_paranoia); -  static efi_status_t virt_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); -	status = efi_call_virt2(get_time, tm, tc); +	status = efi_call_virt(get_time, tm, tc);  	spin_unlock_irqrestore(&rtc_lock, flags);  	return status;  } @@ -134,7 +121,7 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm)  	efi_status_t status;  	spin_lock_irqsave(&rtc_lock, flags); -	status = efi_call_virt1(set_time, tm); +	status = efi_call_virt(set_time, tm);  	spin_unlock_irqrestore(&rtc_lock, flags);  	return status;  } @@ -147,8 +134,7 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled,  	efi_status_t status;  	spin_lock_irqsave(&rtc_lock, flags); -	status = efi_call_virt3(get_wakeup_time, -				enabled, pending, tm); +	status = efi_call_virt(get_wakeup_time, enabled, pending, tm);  	spin_unlock_irqrestore(&rtc_lock, flags);  	return status;  } @@ -159,8 +145,7 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm)  	efi_status_t status;  	spin_lock_irqsave(&rtc_lock, flags); -	status = efi_call_virt2(set_wakeup_time, -				enabled, tm); +	status = efi_call_virt(set_wakeup_time, enabled, tm);  	spin_unlock_irqrestore(&rtc_lock, flags);  	return status;  } @@ -171,17 +156,17 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name,  					  unsigned long *data_size,  					  void *data)  { -	return efi_call_virt5(get_variable, -			      name, vendor, attr, -			      data_size, data); +	return efi_call_virt(get_variable, +			     name, vendor, attr, +			     data_size, data);  }  static efi_status_t virt_efi_get_next_variable(unsigned long *name_size,  					       efi_char16_t *name,  					       efi_guid_t *vendor)  { -	return efi_call_virt3(get_next_variable, -			      name_size, name, vendor); +	return efi_call_virt(get_next_variable, +			     name_size, name, vendor);  }  static efi_status_t virt_efi_set_variable(efi_char16_t *name, @@ -190,9 +175,9 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name,  					  unsigned long data_size,  					  void *data)  { -	return efi_call_virt5(set_variable, -			      name, vendor, attr, -			      data_size, data); +	return efi_call_virt(set_variable, +			     name, vendor, attr, +			     data_size, data);  }  static efi_status_t virt_efi_query_variable_info(u32 attr, @@ -203,13 +188,13 @@ static efi_status_t virt_efi_query_variable_info(u32 attr,  	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); +	return efi_call_virt(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); +	return efi_call_virt(get_next_high_mono_count, count);  }  static void virt_efi_reset_system(int reset_type, @@ -217,8 +202,8 @@ static void virt_efi_reset_system(int reset_type,  				  unsigned long data_size,  				  efi_char16_t *data)  { -	efi_call_virt4(reset_system, reset_type, status, -		       data_size, data); +	__efi_call_virt(reset_system, reset_type, status, +			data_size, data);  }  static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, @@ -228,7 +213,7 @@ static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules,  	if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION)  		return EFI_UNSUPPORTED; -	return efi_call_virt3(update_capsule, capsules, count, sg_list); +	return efi_call_virt(update_capsule, capsules, count, sg_list);  }  static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, @@ -239,8 +224,8 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules,  	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); +	return efi_call_virt(query_capsule_caps, capsules, count, max_size, +			     reset_type);  }  static efi_status_t __init phys_efi_set_virtual_address_map( @@ -252,34 +237,19 @@ static efi_status_t __init phys_efi_set_virtual_address_map(  	efi_status_t status;  	efi_call_phys_prelog(); -	status = efi_call_phys4(efi_phys.set_virtual_address_map, -				memory_map_size, descriptor_size, -				descriptor_version, virtual_map); +	status = efi_call_phys(efi_phys.set_virtual_address_map, +			       memory_map_size, descriptor_size, +			       descriptor_version, virtual_map);  	efi_call_phys_epilog();  	return status;  } -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, virt_to_phys(tm), -				virt_to_phys(tc)); -	efi_call_phys_epilog(); -	spin_unlock_irqrestore(&rtc_lock, flags); -	return status; -} -  int efi_set_rtc_mmss(const struct timespec *now)  {  	unsigned long nowtime = now->tv_sec; -	efi_status_t 	status; -	efi_time_t 	eft; -	efi_time_cap_t 	cap; +	efi_status_t	status; +	efi_time_t	eft; +	efi_time_cap_t	cap;  	struct rtc_time	tm;  	status = efi.get_time(&eft, &cap); @@ -297,9 +267,8 @@ int efi_set_rtc_mmss(const struct timespec *now)  		eft.second = tm.tm_sec;  		eft.nanosecond = 0;  	} else { -		printk(KERN_ERR -		       "%s: Invalid EFI RTC value: write of %lx to EFI RTC failed\n", -		       __FUNCTION__, nowtime); +		pr_err("%s: Invalid EFI RTC value: write of %lx to EFI RTC failed\n", +		       __func__, nowtime);  		return -1;  	} @@ -399,12 +368,14 @@ int __init efi_memblock_x86_reserve_range(void)  	memblock_reserve(pmap, memmap.nr_map * memmap.desc_size); +	efi.memmap = &memmap; +  	return 0;  } -#if EFI_DEBUG  static void __init print_efi_memmap(void)  { +#ifdef EFI_DEBUG  	efi_memory_desc_t *md;  	void *p;  	int i; @@ -413,14 +384,13 @@ static void __init print_efi_memmap(void)  	     p < memmap.map_end;  	     p += memmap.desc_size, i++) {  		md = p; -		pr_info("mem%02u: type=%u, attr=0x%llx, " -			"range=[0x%016llx-0x%016llx) (%lluMB)\n", +		pr_info("mem%02u: type=%u, attr=0x%llx, range=[0x%016llx-0x%016llx) (%lluMB)\n",  			i, md->type, md->attribute, md->phys_addr,  			md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT),  			(md->num_pages >> (20 - EFI_PAGE_SHIFT)));  	} -}  #endif  /*  EFI_DEBUG  */ +}  void __init efi_reserve_boot_services(void)  { @@ -440,15 +410,14 @@ void __init efi_reserve_boot_services(void)  		 * - Not within any part of the kernel  		 * - Not the bios reserved area  		*/ -		if ((start+size >= __pa_symbol(_text) +		if ((start + size > __pa_symbol(_text)  				&& start <= __pa_symbol(_end)) ||  			!e820_all_mapped(start, start+size, E820_RAM) ||  			memblock_is_region_reserved(start, size)) {  			/* Could not reserve, skip it */  			md->num_pages = 0; -			memblock_dbg("Could not reserve boot range " -					"[0x%010llx-0x%010llx]\n", -						start, start+size-1); +			memblock_dbg("Could not reserve boot range [0x%010llx-0x%010llx]\n", +				     start, start+size-1);  		} else  			memblock_reserve(start, size);  	} @@ -456,7 +425,7 @@ void __init efi_reserve_boot_services(void)  void __init efi_unmap_memmap(void)  { -	clear_bit(EFI_MEMMAP, &x86_efi_facility); +	clear_bit(EFI_MEMMAP, &efi.flags);  	if (memmap.map) {  		early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size);  		memmap.map = NULL; @@ -467,9 +436,6 @@ void __init efi_free_boot_services(void)  {  	void *p; -	if (!efi_is_native()) -		return; -  	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {  		efi_memory_desc_t *md = p;  		unsigned long long start = md->phys_addr; @@ -493,18 +459,27 @@ static int __init efi_systab_init(void *phys)  {  	if (efi_enabled(EFI_64BIT)) {  		efi_system_table_64_t *systab64; +		struct efi_setup_data *data = NULL;  		u64 tmp = 0; +		if (efi_setup) { +			data = early_memremap(efi_setup, sizeof(*data)); +			if (!data) +				return -ENOMEM; +		}  		systab64 = early_ioremap((unsigned long)phys,  					 sizeof(*systab64));  		if (systab64 == NULL) {  			pr_err("Couldn't map the system table!\n"); +			if (data) +				early_iounmap(data, sizeof(*data));  			return -ENOMEM;  		}  		efi_systab.hdr = systab64->hdr; -		efi_systab.fw_vendor = systab64->fw_vendor; -		tmp |= systab64->fw_vendor; +		efi_systab.fw_vendor = data ? (unsigned long)data->fw_vendor : +					      systab64->fw_vendor; +		tmp |= data ? data->fw_vendor : systab64->fw_vendor;  		efi_systab.fw_revision = systab64->fw_revision;  		efi_systab.con_in_handle = systab64->con_in_handle;  		tmp |= systab64->con_in_handle; @@ -518,15 +493,20 @@ static int __init efi_systab_init(void *phys)  		tmp |= systab64->stderr_handle;  		efi_systab.stderr = systab64->stderr;  		tmp |= systab64->stderr; -		efi_systab.runtime = (void *)(unsigned long)systab64->runtime; -		tmp |= systab64->runtime; +		efi_systab.runtime = data ? +				     (void *)(unsigned long)data->runtime : +				     (void *)(unsigned long)systab64->runtime; +		tmp |= data ? data->runtime : systab64->runtime;  		efi_systab.boottime = (void *)(unsigned long)systab64->boottime;  		tmp |= systab64->boottime;  		efi_systab.nr_tables = systab64->nr_tables; -		efi_systab.tables = systab64->tables; -		tmp |= systab64->tables; +		efi_systab.tables = data ? (unsigned long)data->tables : +					   systab64->tables; +		tmp |= data ? data->tables : systab64->tables;  		early_iounmap(systab64, sizeof(*systab64)); +		if (data) +			early_iounmap(data, sizeof(*data));  #ifdef CONFIG_X86_32  		if (tmp >> 32) {  			pr_err("EFI data located above 4GB, disabling EFI.\n"); @@ -570,119 +550,82 @@ static int __init efi_systab_init(void *phys)  		return -EINVAL;  	}  	if ((efi.systab->hdr.revision >> 16) == 0) -		pr_err("Warning: System table version " -		       "%d.%02d, expected 1.00 or greater!\n", +		pr_err("Warning: System table version %d.%02d, expected 1.00 or greater!\n",  		       efi.systab->hdr.revision >> 16,  		       efi.systab->hdr.revision & 0xffff); +	set_bit(EFI_SYSTEM_TABLES, &efi.flags); +  	return 0;  } -static int __init efi_config_init(u64 tables, int nr_tables) +static int __init efi_runtime_init32(void)  { -	void *config_tables, *tablep; -	int i, sz; - -	if (efi_enabled(EFI_64BIT)) -		sz = sizeof(efi_config_table_64_t); -	else -		sz = sizeof(efi_config_table_32_t); +	efi_runtime_services_32_t *runtime; -	/* -	 * Let's see what config tables the firmware passed to us. -	 */ -	config_tables = early_ioremap(tables, nr_tables * sz); -	if (config_tables == NULL) { -		pr_err("Could not map Configuration table!\n"); +	runtime = early_ioremap((unsigned long)efi.systab->runtime, +			sizeof(efi_runtime_services_32_t)); +	if (!runtime) { +		pr_err("Could not map the runtime service table!\n");  		return -ENOMEM;  	} -	tablep = config_tables; -	pr_info(""); -	for (i = 0; i < efi.systab->nr_tables; i++) { -		efi_guid_t guid; -		unsigned long table; +	/* +	 * We will only need *early* access to the following two +	 * EFI runtime services before set_virtual_address_map +	 * is invoked. +	 */ +	efi_phys.set_virtual_address_map = +			(efi_set_virtual_address_map_t *) +			(unsigned long)runtime->set_virtual_address_map; +	early_iounmap(runtime, sizeof(efi_runtime_services_32_t)); -		if (efi_enabled(EFI_64BIT)) { -			u64 table64; -			guid = ((efi_config_table_64_t *)tablep)->guid; -			table64 = ((efi_config_table_64_t *)tablep)->table; -			table = table64; -#ifdef CONFIG_X86_32 -			if (table64 >> 32) { -				pr_cont("\n"); -				pr_err("Table located above 4GB, disabling EFI.\n"); -				early_iounmap(config_tables, -					      efi.systab->nr_tables * sz); -				return -EINVAL; -			} -#endif -		} else { -			guid = ((efi_config_table_32_t *)tablep)->guid; -			table = ((efi_config_table_32_t *)tablep)->table; -		} -		if (!efi_guidcmp(guid, MPS_TABLE_GUID)) { -			efi.mps = table; -			pr_cont(" MPS=0x%lx ", table); -		} else if (!efi_guidcmp(guid, ACPI_20_TABLE_GUID)) { -			efi.acpi20 = table; -			pr_cont(" ACPI 2.0=0x%lx ", table); -		} else if (!efi_guidcmp(guid, ACPI_TABLE_GUID)) { -			efi.acpi = table; -			pr_cont(" ACPI=0x%lx ", table); -		} else if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID)) { -			efi.smbios = table; -			pr_cont(" SMBIOS=0x%lx ", table); -#ifdef CONFIG_X86_UV -		} else if (!efi_guidcmp(guid, UV_SYSTEM_TABLE_GUID)) { -			efi.uv_systab = table; -			pr_cont(" UVsystab=0x%lx ", table); -#endif -		} else if (!efi_guidcmp(guid, HCDP_TABLE_GUID)) { -			efi.hcdp = table; -			pr_cont(" HCDP=0x%lx ", table); -		} else if (!efi_guidcmp(guid, UGA_IO_PROTOCOL_GUID)) { -			efi.uga = table; -			pr_cont(" UGA=0x%lx ", table); -		} -		tablep += sz; -	} -	pr_cont("\n"); -	early_iounmap(config_tables, efi.systab->nr_tables * sz);  	return 0;  } -static int __init efi_runtime_init(void) +static int __init efi_runtime_init64(void)  { -	efi_runtime_services_t *runtime; +	efi_runtime_services_64_t *runtime; -	/* -	 * Check out the runtime services table. We need to map -	 * the runtime services table so that we can grab the physical -	 * address of several of the EFI runtime functions, needed to -	 * set the firmware into virtual mode. -	 */  	runtime = early_ioremap((unsigned long)efi.systab->runtime, -				sizeof(efi_runtime_services_t)); +			sizeof(efi_runtime_services_64_t));  	if (!runtime) {  		pr_err("Could not map the runtime service table!\n");  		return -ENOMEM;  	} +  	/* -	 * We will only need *early* access to the following -	 * two EFI runtime services before set_virtual_address_map +	 * We will only need *early* access to the following two +	 * EFI runtime services before set_virtual_address_map  	 * is invoked.  	 */ -	efi_phys.get_time = (efi_get_time_t *)runtime->get_time;  	efi_phys.set_virtual_address_map = -		(efi_set_virtual_address_map_t *) -		runtime->set_virtual_address_map; +			(efi_set_virtual_address_map_t *) +			(unsigned long)runtime->set_virtual_address_map; +	early_iounmap(runtime, sizeof(efi_runtime_services_64_t)); + +	return 0; +} + +static int __init efi_runtime_init(void) +{ +	int rv; +  	/* -	 * Make efi_get_time can be called before entering -	 * virtual mode. +	 * Check out the runtime services table. We need to map +	 * the runtime services table so that we can grab the physical +	 * address of several of the EFI runtime functions, needed to +	 * set the firmware into virtual mode.  	 */ -	efi.get_time = phys_efi_get_time; -	early_iounmap(runtime, sizeof(efi_runtime_services_t)); +	if (efi_enabled(EFI_64BIT)) +		rv = efi_runtime_init64(); +	else +		rv = efi_runtime_init32(); + +	if (rv) +		return rv; + +	set_bit(EFI_RUNTIME_SERVICES, &efi.flags);  	return 0;  } @@ -701,9 +644,67 @@ static int __init efi_memmap_init(void)  	if (add_efi_memmap)  		do_add_efi_memmap(); +	set_bit(EFI_MEMMAP, &efi.flags); +  	return 0;  } +/* + * A number of config table entries get remapped to virtual addresses + * after entering EFI virtual mode. However, the kexec kernel requires + * their physical addresses therefore we pass them via setup_data and + * correct those entries to their respective physical addresses here. + * + * Currently only handles smbios which is necessary for some firmware + * implementation. + */ +static int __init efi_reuse_config(u64 tables, int nr_tables) +{ +	int i, sz, ret = 0; +	void *p, *tablep; +	struct efi_setup_data *data; + +	if (!efi_setup) +		return 0; + +	if (!efi_enabled(EFI_64BIT)) +		return 0; + +	data = early_memremap(efi_setup, sizeof(*data)); +	if (!data) { +		ret = -ENOMEM; +		goto out; +	} + +	if (!data->smbios) +		goto out_memremap; + +	sz = sizeof(efi_config_table_64_t); + +	p = tablep = early_memremap(tables, nr_tables * sz); +	if (!p) { +		pr_err("Could not map Configuration table!\n"); +		ret = -ENOMEM; +		goto out_memremap; +	} + +	for (i = 0; i < efi.systab->nr_tables; i++) { +		efi_guid_t guid; + +		guid = ((efi_config_table_64_t *)p)->guid; + +		if (!efi_guidcmp(guid, SMBIOS_TABLE_GUID)) +			((efi_config_table_64_t *)p)->table = data->smbios; +		p += sz; +	} +	early_iounmap(tablep, nr_tables * sz); + +out_memremap: +	early_iounmap(data, sizeof(*data)); +out: +	return ret; +} +  void __init efi_init(void)  {  	efi_char16_t *c16; @@ -727,7 +728,11 @@ void __init efi_init(void)  	if (efi_systab_init(efi_phys.systab))  		return; -	set_bit(EFI_SYSTEM_TABLES, &x86_efi_facility); +	set_bit(EFI_SYSTEM_TABLES, &efi.flags); + +	efi.config_table = (unsigned long)efi.systab->tables; +	efi.fw_vendor	 = (unsigned long)efi.systab->fw_vendor; +	efi.runtime	 = (unsigned long)efi.systab->runtime;  	/*  	 * Show what we know for posterity @@ -745,39 +750,29 @@ void __init efi_init(void)  		efi.systab->hdr.revision >> 16,  		efi.systab->hdr.revision & 0xffff, vendor); -	if (efi_config_init(efi.systab->tables, efi.systab->nr_tables)) +	if (efi_reuse_config(efi.systab->tables, efi.systab->nr_tables))  		return; -	set_bit(EFI_CONFIG_TABLES, &x86_efi_facility); +	if (efi_config_init(arch_tables)) +		return;  	/*  	 * Note: We currently don't support runtime services on an EFI  	 * that doesn't match the kernel 32/64-bit mode.  	 */ -	if (!efi_is_native()) +	if (!efi_runtime_supported())  		pr_info("No EFI runtime due to 32/64-bit mismatch with kernel\n");  	else {  		if (disable_runtime || efi_runtime_init())  			return; -		set_bit(EFI_RUNTIME_SERVICES, &x86_efi_facility);  	} -  	if (efi_memmap_init())  		return; -	set_bit(EFI_MEMMAP, &x86_efi_facility); +	set_bit(EFI_MEMMAP, &efi.flags); -#ifdef CONFIG_X86_32 -	if (efi_is_native()) { -		x86_platform.get_wallclock = efi_get_time; -		x86_platform.set_wallclock = efi_set_rtc_mmss; -	} -#endif - -#if EFI_DEBUG  	print_efi_memmap(); -#endif  }  void __init efi_late_init(void) @@ -800,7 +795,7 @@ void __init efi_set_executable(efi_memory_desc_t *md, bool executable)  		set_memory_nx(addr, npages);  } -static void __init runtime_code_page_mkexec(void) +void __init runtime_code_page_mkexec(void)  {  	efi_memory_desc_t *md;  	void *p; @@ -816,34 +811,6 @@ static void __init runtime_code_page_mkexec(void)  	}  } -/* - * We can't ioremap data in EFI boot services RAM, because we've already mapped - * it as RAM.  So, look it up in the existing EFI memory map instead.  Only - * callable after efi_enter_virtual_mode and before efi_free_boot_services. - */ -void __iomem *efi_lookup_mapped_addr(u64 phys_addr) -{ -	void *p; -	if (WARN_ON(!memmap.map)) -		return NULL; -	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { -		efi_memory_desc_t *md = p; -		u64 size = md->num_pages << EFI_PAGE_SHIFT; -		u64 end = md->phys_addr + size; -		if (!(md->attribute & EFI_MEMORY_RUNTIME) && -		    md->type != EFI_BOOT_SERVICES_CODE && -		    md->type != EFI_BOOT_SERVICES_DATA) -			continue; -		if (!md->virt_addr) -			continue; -		if (phys_addr >= md->phys_addr && phys_addr < end) { -			phys_addr += md->virt_addr - md->phys_addr; -			return (__force void __iomem *)(unsigned long)phys_addr; -		} -	} -	return NULL; -} -  void efi_memory_uc(u64 addr, unsigned long size)  {  	unsigned long page_shift = 1UL << EFI_PAGE_SHIFT; @@ -854,36 +821,54 @@ void efi_memory_uc(u64 addr, unsigned long size)  	set_memory_uc(addr, npages);  } -/* - * This function will switch the EFI runtime services to virtual mode. - * Essentially, look through the EFI memmap and map every region that - * has the runtime attribute bit set in its memory descriptor and update - * that memory descriptor with the virtual address obtained from ioremap(). - * This enables the runtime services to be called without having to - * thunk back into physical mode for every invocation. - */ -void __init efi_enter_virtual_mode(void) +void __init old_map_region(efi_memory_desc_t *md)  { -	efi_memory_desc_t *md, *prev_md = NULL; -	efi_status_t status; +	u64 start_pfn, end_pfn, end;  	unsigned long size; -	u64 end, systab, start_pfn, end_pfn; -	void *p, *va, *new_memmap = NULL; -	int count = 0; +	void *va; -	efi.systab = NULL; +	start_pfn = PFN_DOWN(md->phys_addr); +	size	  = md->num_pages << PAGE_SHIFT; +	end	  = md->phys_addr + size; +	end_pfn   = PFN_UP(end); -	/* -	 * We don't do virtual mode, since we don't do runtime services, on -	 * non-native EFI -	 */ +	if (pfn_range_is_mapped(start_pfn, end_pfn)) { +		va = __va(md->phys_addr); -	if (!efi_is_native()) { -		efi_unmap_memmap(); -		return; -	} +		if (!(md->attribute & EFI_MEMORY_WB)) +			efi_memory_uc((u64)(unsigned long)va, size); +	} else +		va = efi_ioremap(md->phys_addr, size, +				 md->type, md->attribute); + +	md->virt_addr = (u64) (unsigned long) va; +	if (!va) +		pr_err("ioremap of 0x%llX failed!\n", +		       (unsigned long long)md->phys_addr); +} + +static void native_runtime_setup(void) +{ +	efi.get_time = virt_efi_get_time; +	efi.set_time = virt_efi_set_time; +	efi.get_wakeup_time = virt_efi_get_wakeup_time; +	efi.set_wakeup_time = virt_efi_set_wakeup_time; +	efi.get_variable = virt_efi_get_variable; +	efi.get_next_variable = virt_efi_get_next_variable; +	efi.set_variable = virt_efi_set_variable; +	efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; +	efi.reset_system = virt_efi_reset_system; +	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; +} + +/* Merge contiguous regions of the same type and attribute */ +static void __init efi_merge_regions(void) +{ +	void *p; +	efi_memory_desc_t *md, *prev_md = NULL; -	/* Merge contiguous regions of the same type and attribute */  	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {  		u64 prev_size;  		md = p; @@ -909,6 +894,87 @@ void __init efi_enter_virtual_mode(void)  		}  		prev_md = md;  	} +} + +static void __init get_systab_virt_addr(efi_memory_desc_t *md) +{ +	unsigned long size; +	u64 end, systab; + +	size = md->num_pages << EFI_PAGE_SHIFT; +	end = md->phys_addr + size; +	systab = (u64)(unsigned long)efi_phys.systab; +	if (md->phys_addr <= systab && systab < end) { +		systab += md->virt_addr - md->phys_addr; +		efi.systab = (efi_system_table_t *)(unsigned long)systab; +	} +} + +static void __init save_runtime_map(void) +{ +#ifdef CONFIG_KEXEC +	efi_memory_desc_t *md; +	void *tmp, *p, *q = NULL; +	int count = 0; + +	if (efi_enabled(EFI_OLD_MEMMAP)) +		return; + +	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { +		md = p; + +		if (!(md->attribute & EFI_MEMORY_RUNTIME) || +		    (md->type == EFI_BOOT_SERVICES_CODE) || +		    (md->type == EFI_BOOT_SERVICES_DATA)) +			continue; +		tmp = krealloc(q, (count + 1) * memmap.desc_size, GFP_KERNEL); +		if (!tmp) +			goto out; +		q = tmp; + +		memcpy(q + count * memmap.desc_size, md, memmap.desc_size); +		count++; +	} + +	efi_runtime_map_setup(q, count, memmap.desc_size); +	return; + +out: +	kfree(q); +	pr_err("Error saving runtime map, efi runtime on kexec non-functional!!\n"); +#endif +} + +static void *realloc_pages(void *old_memmap, int old_shift) +{ +	void *ret; + +	ret = (void *)__get_free_pages(GFP_KERNEL, old_shift + 1); +	if (!ret) +		goto out; + +	/* +	 * A first-time allocation doesn't have anything to copy. +	 */ +	if (!old_memmap) +		return ret; + +	memcpy(ret, old_memmap, PAGE_SIZE << old_shift); + +out: +	free_pages((unsigned long)old_memmap, old_shift); +	return ret; +} + +/* + * Map the efi memory ranges of the runtime services and update new_mmap with + * virtual addresses. + */ +static void * __init efi_map_regions(int *count, int *pg_shift) +{ +	void *p, *new_memmap = NULL; +	unsigned long left = 0; +	efi_memory_desc_t *md;  	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {  		md = p; @@ -920,52 +986,150 @@ void __init efi_enter_virtual_mode(void)  				continue;  		} -		size = md->num_pages << EFI_PAGE_SHIFT; -		end = md->phys_addr + size; +		efi_map_region(md); +		get_systab_virt_addr(md); -		start_pfn = PFN_DOWN(md->phys_addr); -		end_pfn = PFN_UP(end); -		if (pfn_range_is_mapped(start_pfn, end_pfn)) { -			va = __va(md->phys_addr); +		if (left < memmap.desc_size) { +			new_memmap = realloc_pages(new_memmap, *pg_shift); +			if (!new_memmap) +				return NULL; -			if (!(md->attribute & EFI_MEMORY_WB)) -				efi_memory_uc((u64)(unsigned long)va, size); -		} else -			va = efi_ioremap(md->phys_addr, size, -					 md->type, md->attribute); - -		md->virt_addr = (u64) (unsigned long) va; - -		if (!va) { -			pr_err("ioremap of 0x%llX failed!\n", -			       (unsigned long long)md->phys_addr); -			continue; +			left += PAGE_SIZE << *pg_shift; +			(*pg_shift)++;  		} -		systab = (u64) (unsigned long) efi_phys.systab; -		if (md->phys_addr <= systab && systab < end) { -			systab += md->virt_addr - md->phys_addr; -			efi.systab = (efi_system_table_t *) (unsigned long) systab; -		} -		new_memmap = krealloc(new_memmap, -				      (count + 1) * memmap.desc_size, -				      GFP_KERNEL); -		memcpy(new_memmap + (count * memmap.desc_size), md, +		memcpy(new_memmap + (*count * memmap.desc_size), md,  		       memmap.desc_size); -		count++; + +		left -= memmap.desc_size; +		(*count)++; +	} + +	return new_memmap; +} + +static void __init kexec_enter_virtual_mode(void) +{ +#ifdef CONFIG_KEXEC +	efi_memory_desc_t *md; +	void *p; + +	efi.systab = NULL; + +	/* +	 * We don't do virtual mode, since we don't do runtime services, on +	 * non-native EFI +	 */ +	if (!efi_is_native()) { +		efi_unmap_memmap(); +		return; +	} + +	/* +	* Map efi regions which were passed via setup_data. The virt_addr is a +	* fixed addr which was used in first kernel of a kexec boot. +	*/ +	for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) { +		md = p; +		efi_map_region_fixed(md); /* FIXME: add error handling */ +		get_systab_virt_addr(md);  	} +	save_runtime_map(); +  	BUG_ON(!efi.systab); -	status = phys_efi_set_virtual_address_map( -		memmap.desc_size * count, -		memmap.desc_size, -		memmap.desc_version, -		(efi_memory_desc_t *)__pa(new_memmap)); +	efi_sync_low_kernel_mappings(); + +	/* +	 * Now that EFI is in virtual mode, update the function +	 * pointers in the runtime service table to the new virtual addresses. +	 * +	 * Call EFI services through wrapper functions. +	 */ +	efi.runtime_version = efi_systab.hdr.revision; + +	native_runtime_setup(); + +	efi.set_virtual_address_map = NULL; + +	if (efi_enabled(EFI_OLD_MEMMAP) && (__supported_pte_mask & _PAGE_NX)) +		runtime_code_page_mkexec(); + +	/* clean DUMMY object */ +	efi.set_variable(efi_dummy_name, &EFI_DUMMY_GUID, +			 EFI_VARIABLE_NON_VOLATILE | +			 EFI_VARIABLE_BOOTSERVICE_ACCESS | +			 EFI_VARIABLE_RUNTIME_ACCESS, +			 0, NULL); +#endif +} + +/* + * This function will switch the EFI runtime services to virtual mode. + * Essentially, we look through the EFI memmap and map every region that + * has the runtime attribute bit set in its memory descriptor into the + * ->trampoline_pgd page table using a top-down VA allocation scheme. + * + * The old method which used to update that memory descriptor with the + * virtual address obtained from ioremap() is still supported when the + * kernel is booted with efi=old_map on its command line. Same old + * method enabled the runtime services to be called without having to + * thunk back into physical mode for every invocation. + * + * The new method does a pagetable switch in a preemption-safe manner + * so that we're in a different address space when calling a runtime + * function. For function arguments passing we do copy the PGDs of the + * kernel page table into ->trampoline_pgd prior to each call. + * + * Specially for kexec boot, efi runtime maps in previous kernel should + * be passed in via setup_data. In that case runtime ranges will be mapped + * to the same virtual addresses as the first kernel, see + * kexec_enter_virtual_mode(). + */ +static void __init __efi_enter_virtual_mode(void) +{ +	int count = 0, pg_shift = 0; +	void *new_memmap = NULL; +	efi_status_t status; + +	efi.systab = NULL; + +	efi_merge_regions(); +	new_memmap = efi_map_regions(&count, &pg_shift); +	if (!new_memmap) { +		pr_err("Error reallocating memory, EFI runtime non-functional!\n"); +		return; +	} + +	save_runtime_map(); + +	BUG_ON(!efi.systab); + +	if (efi_setup_page_tables(__pa(new_memmap), 1 << pg_shift)) +		return; + +	efi_sync_low_kernel_mappings(); +	efi_dump_pagetable(); + +	if (efi_is_native()) { +		status = phys_efi_set_virtual_address_map( +				memmap.desc_size * count, +				memmap.desc_size, +				memmap.desc_version, +				(efi_memory_desc_t *)__pa(new_memmap)); +	} else { +		status = efi_thunk_set_virtual_address_map( +				efi_phys.set_virtual_address_map, +				memmap.desc_size * count, +				memmap.desc_size, +				memmap.desc_version, +				(efi_memory_desc_t *)__pa(new_memmap)); +	}  	if (status != EFI_SUCCESS) { -		pr_alert("Unable to switch EFI into virtual mode " -			 "(status=%lx)!\n", status); +		pr_alert("Unable to switch EFI into virtual mode (status=%lx)!\n", +			 status);  		panic("EFI call to SetVirtualAddressMap() failed!");  	} @@ -976,23 +1140,43 @@ void __init efi_enter_virtual_mode(void)  	 * Call EFI services through wrapper functions.  	 */  	efi.runtime_version = efi_systab.hdr.revision; -	efi.get_time = virt_efi_get_time; -	efi.set_time = virt_efi_set_time; -	efi.get_wakeup_time = virt_efi_get_wakeup_time; -	efi.set_wakeup_time = virt_efi_set_wakeup_time; -	efi.get_variable = virt_efi_get_variable; -	efi.get_next_variable = virt_efi_get_next_variable; -	efi.set_variable = virt_efi_set_variable; -	efi.get_next_high_mono_count = virt_efi_get_next_high_mono_count; -	efi.reset_system = virt_efi_reset_system; + +	if (efi_is_native()) +		native_runtime_setup(); +	else +		efi_thunk_runtime_setup(); +  	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(); -	kfree(new_memmap); +	efi_runtime_mkexec(); + +	/* +	 * We mapped the descriptor array into the EFI pagetable above but we're +	 * not unmapping it here. Here's why: +	 * +	 * We're copying select PGDs from the kernel page table to the EFI page +	 * table and when we do so and make changes to those PGDs like unmapping +	 * stuff from them, those changes appear in the kernel page table and we +	 * go boom. +	 * +	 * From setup_real_mode(): +	 * +	 * ... +	 * trampoline_pgd[0] = init_level4_pgt[pgd_index(__PAGE_OFFSET)].pgd; +	 * +	 * In this particular case, our allocation is in PGD 0 of the EFI page +	 * table but we've copied that PGD from PGD[272] of the EFI page table: +	 * +	 *	pgd_index(__PAGE_OFFSET = 0xffff880000000000) = 272 +	 * +	 * where the direct memory mapping in kernel space is. +	 * +	 * new_memmap's VA comes from that direct mapping and thus clearing it, +	 * it would get cleared in the kernel page table too. +	 * +	 * efi_cleanup_page_tables(__pa(new_memmap), 1 << pg_shift); +	 */ +	free_pages((unsigned long)new_memmap, pg_shift);  	/* clean DUMMY object */  	efi.set_variable(efi_dummy_name, &EFI_DUMMY_GUID, @@ -1002,6 +1186,14 @@ void __init efi_enter_virtual_mode(void)  			 0, NULL);  } +void __init efi_enter_virtual_mode(void) +{ +	if (efi_setup) +		kexec_enter_virtual_mode(); +	else +		__efi_enter_virtual_mode(); +} +  /*   * Convenience functions to obtain memory types and attributes   */ @@ -1039,9 +1231,8 @@ u64 efi_mem_attributes(unsigned long phys_addr)  }  /* - * Some firmware has serious problems when using more than 50% of the EFI - * variable store, i.e. it triggers bugs that can brick machines. Ensure that - * we never use more than this safe limit. + * Some firmware implementations refuse to boot if there's insufficient space + * in the variable store. Ensure that we never use more than a safe limit.   *   * Return EFI_SUCCESS if it is safe to write 'size' bytes to the variable   * store. @@ -1060,10 +1251,9 @@ efi_status_t efi_query_variable_store(u32 attributes, unsigned long size)  		return status;  	/* -	 * Some firmware implementations refuse to boot if there's insufficient -	 * space in the variable store. We account for that by refusing the -	 * write if permitting it would reduce the available space to under -	 * 5KB. This figure was provided by Samsung, so should be safe. +	 * We account for that by refusing the write if permitting it would +	 * reduce the available space to under 5KB. This figure was provided by +	 * Samsung, so should be safe.  	 */  	if ((remaining_size - size < EFI_MIN_RESERVE) &&  		!efi_no_storage_paranoia) { @@ -1119,3 +1309,34 @@ efi_status_t efi_query_variable_store(u32 attributes, unsigned long size)  	return EFI_SUCCESS;  }  EXPORT_SYMBOL_GPL(efi_query_variable_store); + +static int __init parse_efi_cmdline(char *str) +{ +	if (*str == '=') +		str++; + +	if (!strncmp(str, "old_map", 7)) +		set_bit(EFI_OLD_MEMMAP, &efi.flags); + +	return 0; +} +early_param("efi", parse_efi_cmdline); + +void __init efi_apply_memmap_quirks(void) +{ +	/* +	 * Once setup is done earlier, unmap the EFI memory map on mismatched +	 * firmware/kernel architectures since there is no support for runtime +	 * services. +	 */ +	if (!efi_runtime_supported()) { +		pr_info("efi: Setup done, disabling due to 32/64-bit mismatch\n"); +		efi_unmap_memmap(); +	} + +	/* +	 * UV doesn't support the new EFI pagetable mapping yet. +	 */ +	if (is_uv_system()) +		set_bit(EFI_OLD_MEMMAP, &efi.flags); +} diff --git a/arch/x86/platform/efi/efi_32.c b/arch/x86/platform/efi/efi_32.c index 40e446941dd..9ee3491e31f 100644 --- a/arch/x86/platform/efi/efi_32.c +++ b/arch/x86/platform/efi/efi_32.c @@ -37,9 +37,24 @@   * claim EFI runtime service handler exclusively and to duplicate a memory in   * low memory space say 0 - 3G.   */ -  static unsigned long efi_rt_eflags; +void efi_sync_low_kernel_mappings(void) {} +void __init efi_dump_pagetable(void) {} +int efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages) +{ +	return 0; +} +void efi_cleanup_page_tables(unsigned long pa_memmap, unsigned num_pages) {} + +void __init efi_map_region(efi_memory_desc_t *md) +{ +	old_map_region(md); +} + +void __init efi_map_region_fixed(efi_memory_desc_t *md) {} +void __init parse_efi_setup(u64 phys_addr, u32 data_len) {} +  void efi_call_phys_prelog(void)  {  	struct desc_ptr gdt_descr; @@ -67,3 +82,9 @@ void efi_call_phys_epilog(void)  	local_irq_restore(efi_rt_eflags);  } + +void __init efi_runtime_mkexec(void) +{ +	if (__supported_pte_mask & _PAGE_NX) +		runtime_code_page_mkexec(); +} diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c index 39a0e7f1f0a..290d397e1dd 100644 --- a/arch/x86/platform/efi/efi_64.c +++ b/arch/x86/platform/efi/efi_64.c @@ -38,10 +38,30 @@  #include <asm/efi.h>  #include <asm/cacheflush.h>  #include <asm/fixmap.h> +#include <asm/realmode.h> +#include <asm/time.h>  static pgd_t *save_pgd __initdata;  static unsigned long efi_flags __initdata; +/* + * We allocate runtime services regions bottom-up, starting from -4G, i.e. + * 0xffff_ffff_0000_0000 and limit EFI VA mapping space to 64G. + */ +static u64 efi_va	= -4 * (1UL << 30); +#define EFI_VA_END	(-68 * (1UL << 30)) + +/* + * Scratch space used for switching the pagetable in the EFI stub + */ +struct efi_scratch { +	u64 r15; +	u64 prev_cr3; +	pgd_t *efi_pgt; +	bool use_pgd; +	u64 phys_stack; +} __packed; +  static void __init early_code_mapping_set_exec(int executable)  {  	efi_memory_desc_t *md; @@ -65,6 +85,9 @@ void __init efi_call_phys_prelog(void)  	int pgd;  	int n_pgds; +	if (!efi_enabled(EFI_OLD_MEMMAP)) +		return; +  	early_code_mapping_set_exec(1);  	local_irq_save(efi_flags); @@ -86,6 +109,10 @@ void __init efi_call_phys_epilog(void)  	 */  	int pgd;  	int n_pgds = DIV_ROUND_UP((max_pfn << PAGE_SHIFT) , PGDIR_SIZE); + +	if (!efi_enabled(EFI_OLD_MEMMAP)) +		return; +  	for (pgd = 0; pgd < n_pgds; pgd++)  		set_pgd(pgd_offset_k(pgd * PGDIR_SIZE), save_pgd[pgd]);  	kfree(save_pgd); @@ -94,6 +121,158 @@ void __init efi_call_phys_epilog(void)  	early_code_mapping_set_exec(0);  } +/* + * Add low kernel mappings for passing arguments to EFI functions. + */ +void efi_sync_low_kernel_mappings(void) +{ +	unsigned num_pgds; +	pgd_t *pgd = (pgd_t *)__va(real_mode_header->trampoline_pgd); + +	if (efi_enabled(EFI_OLD_MEMMAP)) +		return; + +	num_pgds = pgd_index(MODULES_END - 1) - pgd_index(PAGE_OFFSET); + +	memcpy(pgd + pgd_index(PAGE_OFFSET), +		init_mm.pgd + pgd_index(PAGE_OFFSET), +		sizeof(pgd_t) * num_pgds); +} + +int efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages) +{ +	unsigned long text; +	struct page *page; +	unsigned npages; +	pgd_t *pgd; + +	if (efi_enabled(EFI_OLD_MEMMAP)) +		return 0; + +	efi_scratch.efi_pgt = (pgd_t *)(unsigned long)real_mode_header->trampoline_pgd; +	pgd = __va(efi_scratch.efi_pgt); + +	/* +	 * It can happen that the physical address of new_memmap lands in memory +	 * which is not mapped in the EFI page table. Therefore we need to go +	 * and ident-map those pages containing the map before calling +	 * phys_efi_set_virtual_address_map(). +	 */ +	if (kernel_map_pages_in_pgd(pgd, pa_memmap, pa_memmap, num_pages, _PAGE_NX)) { +		pr_err("Error ident-mapping new memmap (0x%lx)!\n", pa_memmap); +		return 1; +	} + +	efi_scratch.use_pgd = true; + +	/* +	 * When making calls to the firmware everything needs to be 1:1 +	 * mapped and addressable with 32-bit pointers. Map the kernel +	 * text and allocate a new stack because we can't rely on the +	 * stack pointer being < 4GB. +	 */ +	if (!IS_ENABLED(CONFIG_EFI_MIXED)) +		return 0; + +	page = alloc_page(GFP_KERNEL|__GFP_DMA32); +	if (!page) +		panic("Unable to allocate EFI runtime stack < 4GB\n"); + +	efi_scratch.phys_stack = virt_to_phys(page_address(page)); +	efi_scratch.phys_stack += PAGE_SIZE; /* stack grows down */ + +	npages = (_end - _text) >> PAGE_SHIFT; +	text = __pa(_text); + +	if (kernel_map_pages_in_pgd(pgd, text >> PAGE_SHIFT, text, npages, 0)) { +		pr_err("Failed to map kernel text 1:1\n"); +		return 1; +	} + +	return 0; +} + +void efi_cleanup_page_tables(unsigned long pa_memmap, unsigned num_pages) +{ +	pgd_t *pgd = (pgd_t *)__va(real_mode_header->trampoline_pgd); + +	kernel_unmap_pages_in_pgd(pgd, pa_memmap, num_pages); +} + +static void __init __map_region(efi_memory_desc_t *md, u64 va) +{ +	pgd_t *pgd = (pgd_t *)__va(real_mode_header->trampoline_pgd); +	unsigned long pf = 0; + +	if (!(md->attribute & EFI_MEMORY_WB)) +		pf |= _PAGE_PCD; + +	if (kernel_map_pages_in_pgd(pgd, md->phys_addr, va, md->num_pages, pf)) +		pr_warn("Error mapping PA 0x%llx -> VA 0x%llx!\n", +			   md->phys_addr, va); +} + +void __init efi_map_region(efi_memory_desc_t *md) +{ +	unsigned long size = md->num_pages << PAGE_SHIFT; +	u64 pa = md->phys_addr; + +	if (efi_enabled(EFI_OLD_MEMMAP)) +		return old_map_region(md); + +	/* +	 * Make sure the 1:1 mappings are present as a catch-all for b0rked +	 * firmware which doesn't update all internal pointers after switching +	 * to virtual mode and would otherwise crap on us. +	 */ +	__map_region(md, md->phys_addr); + +	/* +	 * Enforce the 1:1 mapping as the default virtual address when +	 * booting in EFI mixed mode, because even though we may be +	 * running a 64-bit kernel, the firmware may only be 32-bit. +	 */ +	if (!efi_is_native () && IS_ENABLED(CONFIG_EFI_MIXED)) { +		md->virt_addr = md->phys_addr; +		return; +	} + +	efi_va -= size; + +	/* Is PA 2M-aligned? */ +	if (!(pa & (PMD_SIZE - 1))) { +		efi_va &= PMD_MASK; +	} else { +		u64 pa_offset = pa & (PMD_SIZE - 1); +		u64 prev_va = efi_va; + +		/* get us the same offset within this 2M page */ +		efi_va = (efi_va & PMD_MASK) + pa_offset; + +		if (efi_va > prev_va) +			efi_va -= PMD_SIZE; +	} + +	if (efi_va < EFI_VA_END) { +		pr_warn(FW_WARN "VA address range overflow!\n"); +		return; +	} + +	/* Do the VA map */ +	__map_region(md, efi_va); +	md->virt_addr = efi_va; +} + +/* + * kexec kernel will use efi_map_region_fixed to map efi runtime memory ranges. + * md->virt_addr is the original virtual address which had been mapped in kexec + * 1st kernel. + */ +void __init efi_map_region_fixed(efi_memory_desc_t *md) +{ +	__map_region(md, md->virt_addr); +} +  void __iomem *__init efi_ioremap(unsigned long phys_addr, unsigned long size,  				 u32 type, u64 attribute)  { @@ -113,3 +292,313 @@ void __iomem *__init efi_ioremap(unsigned long phys_addr, unsigned long size,  	return (void __iomem *)__va(phys_addr);  } + +void __init parse_efi_setup(u64 phys_addr, u32 data_len) +{ +	efi_setup = phys_addr + sizeof(struct setup_data); +} + +void __init efi_runtime_mkexec(void) +{ +	if (!efi_enabled(EFI_OLD_MEMMAP)) +		return; + +	if (__supported_pte_mask & _PAGE_NX) +		runtime_code_page_mkexec(); +} + +void __init efi_dump_pagetable(void) +{ +#ifdef CONFIG_EFI_PGT_DUMP +	pgd_t *pgd = (pgd_t *)__va(real_mode_header->trampoline_pgd); + +	ptdump_walk_pgd_level(NULL, pgd); +#endif +} + +#ifdef CONFIG_EFI_MIXED +extern efi_status_t efi64_thunk(u32, ...); + +#define runtime_service32(func)						 \ +({									 \ +	u32 table = (u32)(unsigned long)efi.systab;			 \ +	u32 *rt, *___f;							 \ +									 \ +	rt = (u32 *)(table + offsetof(efi_system_table_32_t, runtime));	 \ +	___f = (u32 *)(*rt + offsetof(efi_runtime_services_32_t, func)); \ +	*___f;								 \ +}) + +/* + * Switch to the EFI page tables early so that we can access the 1:1 + * runtime services mappings which are not mapped in any other page + * tables. This function must be called before runtime_service32(). + * + * Also, disable interrupts because the IDT points to 64-bit handlers, + * which aren't going to function correctly when we switch to 32-bit. + */ +#define efi_thunk(f, ...)						\ +({									\ +	efi_status_t __s;						\ +	unsigned long flags;						\ +	u32 func;							\ +									\ +	efi_sync_low_kernel_mappings();					\ +	local_irq_save(flags);						\ +									\ +	efi_scratch.prev_cr3 = read_cr3();				\ +	write_cr3((unsigned long)efi_scratch.efi_pgt);			\ +	__flush_tlb_all();						\ +									\ +	func = runtime_service32(f);					\ +	__s = efi64_thunk(func, __VA_ARGS__);			\ +									\ +	write_cr3(efi_scratch.prev_cr3);				\ +	__flush_tlb_all();						\ +	local_irq_restore(flags);					\ +									\ +	__s;								\ +}) + +efi_status_t efi_thunk_set_virtual_address_map( +	void *phys_set_virtual_address_map, +	unsigned long memory_map_size, +	unsigned long descriptor_size, +	u32 descriptor_version, +	efi_memory_desc_t *virtual_map) +{ +	efi_status_t status; +	unsigned long flags; +	u32 func; + +	efi_sync_low_kernel_mappings(); +	local_irq_save(flags); + +	efi_scratch.prev_cr3 = read_cr3(); +	write_cr3((unsigned long)efi_scratch.efi_pgt); +	__flush_tlb_all(); + +	func = (u32)(unsigned long)phys_set_virtual_address_map; +	status = efi64_thunk(func, memory_map_size, descriptor_size, +			     descriptor_version, virtual_map); + +	write_cr3(efi_scratch.prev_cr3); +	__flush_tlb_all(); +	local_irq_restore(flags); + +	return status; +} + +static efi_status_t efi_thunk_get_time(efi_time_t *tm, efi_time_cap_t *tc) +{ +	efi_status_t status; +	u32 phys_tm, phys_tc; + +	spin_lock(&rtc_lock); + +	phys_tm = virt_to_phys(tm); +	phys_tc = virt_to_phys(tc); + +	status = efi_thunk(get_time, phys_tm, phys_tc); + +	spin_unlock(&rtc_lock); + +	return status; +} + +static efi_status_t efi_thunk_set_time(efi_time_t *tm) +{ +	efi_status_t status; +	u32 phys_tm; + +	spin_lock(&rtc_lock); + +	phys_tm = virt_to_phys(tm); + +	status = efi_thunk(set_time, phys_tm); + +	spin_unlock(&rtc_lock); + +	return status; +} + +static efi_status_t +efi_thunk_get_wakeup_time(efi_bool_t *enabled, efi_bool_t *pending, +			  efi_time_t *tm) +{ +	efi_status_t status; +	u32 phys_enabled, phys_pending, phys_tm; + +	spin_lock(&rtc_lock); + +	phys_enabled = virt_to_phys(enabled); +	phys_pending = virt_to_phys(pending); +	phys_tm = virt_to_phys(tm); + +	status = efi_thunk(get_wakeup_time, phys_enabled, +			     phys_pending, phys_tm); + +	spin_unlock(&rtc_lock); + +	return status; +} + +static efi_status_t +efi_thunk_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) +{ +	efi_status_t status; +	u32 phys_tm; + +	spin_lock(&rtc_lock); + +	phys_tm = virt_to_phys(tm); + +	status = efi_thunk(set_wakeup_time, enabled, phys_tm); + +	spin_unlock(&rtc_lock); + +	return status; +} + + +static efi_status_t +efi_thunk_get_variable(efi_char16_t *name, efi_guid_t *vendor, +		       u32 *attr, unsigned long *data_size, void *data) +{ +	efi_status_t status; +	u32 phys_name, phys_vendor, phys_attr; +	u32 phys_data_size, phys_data; + +	phys_data_size = virt_to_phys(data_size); +	phys_vendor = virt_to_phys(vendor); +	phys_name = virt_to_phys(name); +	phys_attr = virt_to_phys(attr); +	phys_data = virt_to_phys(data); + +	status = efi_thunk(get_variable, phys_name, phys_vendor, +			   phys_attr, phys_data_size, phys_data); + +	return status; +} + +static efi_status_t +efi_thunk_set_variable(efi_char16_t *name, efi_guid_t *vendor, +		       u32 attr, unsigned long data_size, void *data) +{ +	u32 phys_name, phys_vendor, phys_data; +	efi_status_t status; + +	phys_name = virt_to_phys(name); +	phys_vendor = virt_to_phys(vendor); +	phys_data = virt_to_phys(data); + +	/* If data_size is > sizeof(u32) we've got problems */ +	status = efi_thunk(set_variable, phys_name, phys_vendor, +			   attr, data_size, phys_data); + +	return status; +} + +static efi_status_t +efi_thunk_get_next_variable(unsigned long *name_size, +			    efi_char16_t *name, +			    efi_guid_t *vendor) +{ +	efi_status_t status; +	u32 phys_name_size, phys_name, phys_vendor; + +	phys_name_size = virt_to_phys(name_size); +	phys_vendor = virt_to_phys(vendor); +	phys_name = virt_to_phys(name); + +	status = efi_thunk(get_next_variable, phys_name_size, +			   phys_name, phys_vendor); + +	return status; +} + +static efi_status_t +efi_thunk_get_next_high_mono_count(u32 *count) +{ +	efi_status_t status; +	u32 phys_count; + +	phys_count = virt_to_phys(count); +	status = efi_thunk(get_next_high_mono_count, phys_count); + +	return status; +} + +static void +efi_thunk_reset_system(int reset_type, efi_status_t status, +		       unsigned long data_size, efi_char16_t *data) +{ +	u32 phys_data; + +	phys_data = virt_to_phys(data); + +	efi_thunk(reset_system, reset_type, status, data_size, phys_data); +} + +static efi_status_t +efi_thunk_update_capsule(efi_capsule_header_t **capsules, +			 unsigned long count, unsigned long sg_list) +{ +	/* +	 * To properly support this function we would need to repackage +	 * 'capsules' because the firmware doesn't understand 64-bit +	 * pointers. +	 */ +	return EFI_UNSUPPORTED; +} + +static efi_status_t +efi_thunk_query_variable_info(u32 attr, u64 *storage_space, +			      u64 *remaining_space, +			      u64 *max_variable_size) +{ +	efi_status_t status; +	u32 phys_storage, phys_remaining, phys_max; + +	if (efi.runtime_version < EFI_2_00_SYSTEM_TABLE_REVISION) +		return EFI_UNSUPPORTED; + +	phys_storage = virt_to_phys(storage_space); +	phys_remaining = virt_to_phys(remaining_space); +	phys_max = virt_to_phys(max_variable_size); + +	status = efi_thunk(query_variable_info, attr, phys_storage, +			   phys_remaining, phys_max); + +	return status; +} + +static efi_status_t +efi_thunk_query_capsule_caps(efi_capsule_header_t **capsules, +			     unsigned long count, u64 *max_size, +			     int *reset_type) +{ +	/* +	 * To properly support this function we would need to repackage +	 * 'capsules' because the firmware doesn't understand 64-bit +	 * pointers. +	 */ +	return EFI_UNSUPPORTED; +} + +void efi_thunk_runtime_setup(void) +{ +	efi.get_time = efi_thunk_get_time; +	efi.set_time = efi_thunk_set_time; +	efi.get_wakeup_time = efi_thunk_get_wakeup_time; +	efi.set_wakeup_time = efi_thunk_set_wakeup_time; +	efi.get_variable = efi_thunk_get_variable; +	efi.get_next_variable = efi_thunk_get_next_variable; +	efi.set_variable = efi_thunk_set_variable; +	efi.get_next_high_mono_count = efi_thunk_get_next_high_mono_count; +	efi.reset_system = efi_thunk_reset_system; +	efi.query_variable_info = efi_thunk_query_variable_info; +	efi.update_capsule = efi_thunk_update_capsule; +	efi.query_capsule_caps = efi_thunk_query_capsule_caps; +} +#endif /* CONFIG_EFI_MIXED */ diff --git a/arch/x86/platform/efi/efi_stub_64.S b/arch/x86/platform/efi/efi_stub_64.S index 4c07ccab814..5fcda727255 100644 --- a/arch/x86/platform/efi/efi_stub_64.S +++ b/arch/x86/platform/efi/efi_stub_64.S @@ -7,6 +7,10 @@   */  #include <linux/linkage.h> +#include <asm/segment.h> +#include <asm/msr.h> +#include <asm/processor-flags.h> +#include <asm/page_types.h>  #define SAVE_XMM			\  	mov %rsp, %rax;			\ @@ -34,72 +38,42 @@  	mov %rsi, %cr0;			\  	mov (%rsp), %rsp -ENTRY(efi_call0) -	SAVE_XMM -	subq $32, %rsp -	call *%rdi -	addq $32, %rsp -	RESTORE_XMM -	ret -ENDPROC(efi_call0) +	/* stolen from gcc */ +	.macro FLUSH_TLB_ALL +	movq %r15, efi_scratch(%rip) +	movq %r14, efi_scratch+8(%rip) +	movq %cr4, %r15 +	movq %r15, %r14 +	andb $0x7f, %r14b +	movq %r14, %cr4 +	movq %r15, %cr4 +	movq efi_scratch+8(%rip), %r14 +	movq efi_scratch(%rip), %r15 +	.endm -ENTRY(efi_call1) -	SAVE_XMM -	subq $32, %rsp -	mov  %rsi, %rcx -	call *%rdi -	addq $32, %rsp -	RESTORE_XMM -	ret -ENDPROC(efi_call1) +	.macro SWITCH_PGT +	cmpb $0, efi_scratch+24(%rip) +	je 1f +	movq %r15, efi_scratch(%rip)		# r15 +	# save previous CR3 +	movq %cr3, %r15 +	movq %r15, efi_scratch+8(%rip)		# prev_cr3 +	movq efi_scratch+16(%rip), %r15		# EFI pgt +	movq %r15, %cr3 +	1: +	.endm -ENTRY(efi_call2) -	SAVE_XMM -	subq $32, %rsp -	mov  %rsi, %rcx -	call *%rdi -	addq $32, %rsp -	RESTORE_XMM -	ret -ENDPROC(efi_call2) +	.macro RESTORE_PGT +	cmpb $0, efi_scratch+24(%rip) +	je 2f +	movq efi_scratch+8(%rip), %r15 +	movq %r15, %cr3 +	movq efi_scratch(%rip), %r15 +	FLUSH_TLB_ALL +	2: +	.endm -ENTRY(efi_call3) -	SAVE_XMM -	subq $32, %rsp -	mov  %rcx, %r8 -	mov  %rsi, %rcx -	call *%rdi -	addq $32, %rsp -	RESTORE_XMM -	ret -ENDPROC(efi_call3) - -ENTRY(efi_call4) -	SAVE_XMM -	subq $32, %rsp -	mov %r8, %r9 -	mov %rcx, %r8 -	mov %rsi, %rcx -	call *%rdi -	addq $32, %rsp -	RESTORE_XMM -	ret -ENDPROC(efi_call4) - -ENTRY(efi_call5) -	SAVE_XMM -	subq $48, %rsp -	mov %r9, 32(%rsp) -	mov %r8, %r9 -	mov %rcx, %r8 -	mov %rsi, %rcx -	call *%rdi -	addq $48, %rsp -	RESTORE_XMM -	ret -ENDPROC(efi_call5) - -ENTRY(efi_call6) +ENTRY(efi_call)  	SAVE_XMM  	mov (%rsp), %rax  	mov 8(%rax), %rax @@ -109,8 +83,177 @@ ENTRY(efi_call6)  	mov %r8, %r9  	mov %rcx, %r8  	mov %rsi, %rcx +	SWITCH_PGT  	call *%rdi +	RESTORE_PGT  	addq $48, %rsp  	RESTORE_XMM  	ret -ENDPROC(efi_call6) +ENDPROC(efi_call) + +#ifdef CONFIG_EFI_MIXED + +/* + * We run this function from the 1:1 mapping. + * + * This function must be invoked with a 1:1 mapped stack. + */ +ENTRY(__efi64_thunk) +	movl	%ds, %eax +	push	%rax +	movl	%es, %eax +	push	%rax +	movl	%ss, %eax +	push	%rax + +	subq	$32, %rsp +	movl	%esi, 0x0(%rsp) +	movl	%edx, 0x4(%rsp) +	movl	%ecx, 0x8(%rsp) +	movq	%r8, %rsi +	movl	%esi, 0xc(%rsp) +	movq	%r9, %rsi +	movl	%esi,  0x10(%rsp) + +	sgdt	save_gdt(%rip) + +	leaq	1f(%rip), %rbx +	movq	%rbx, func_rt_ptr(%rip) + +	/* Switch to gdt with 32-bit segments */ +	movl	64(%rsp), %eax +	lgdt	(%rax) + +	leaq	efi_enter32(%rip), %rax +	pushq	$__KERNEL_CS +	pushq	%rax +	lretq + +1:	addq	$32, %rsp + +	lgdt	save_gdt(%rip) + +	pop	%rbx +	movl	%ebx, %ss +	pop	%rbx +	movl	%ebx, %es +	pop	%rbx +	movl	%ebx, %ds + +	/* +	 * Convert 32-bit status code into 64-bit. +	 */ +	test	%rax, %rax +	jz	1f +	movl	%eax, %ecx +	andl	$0x0fffffff, %ecx +	andl	$0xf0000000, %eax +	shl	$32, %rax +	or	%rcx, %rax +1: +	ret +ENDPROC(__efi64_thunk) + +ENTRY(efi_exit32) +	movq	func_rt_ptr(%rip), %rax +	push	%rax +	mov	%rdi, %rax +	ret +ENDPROC(efi_exit32) + +	.code32 +/* + * EFI service pointer must be in %edi. + * + * The stack should represent the 32-bit calling convention. + */ +ENTRY(efi_enter32) +	movl	$__KERNEL_DS, %eax +	movl	%eax, %ds +	movl	%eax, %es +	movl	%eax, %ss + +	/* Reload pgtables */ +	movl	%cr3, %eax +	movl	%eax, %cr3 + +	/* Disable paging */ +	movl	%cr0, %eax +	btrl	$X86_CR0_PG_BIT, %eax +	movl	%eax, %cr0 + +	/* Disable long mode via EFER */ +	movl	$MSR_EFER, %ecx +	rdmsr +	btrl	$_EFER_LME, %eax +	wrmsr + +	call	*%edi + +	/* We must preserve return value */ +	movl	%eax, %edi + +	/* +	 * Some firmware will return with interrupts enabled. Be sure to +	 * disable them before we switch GDTs. +	 */ +	cli + +	movl	68(%esp), %eax +	movl	%eax, 2(%eax) +	lgdtl	(%eax) + +	movl	%cr4, %eax +	btsl	$(X86_CR4_PAE_BIT), %eax +	movl	%eax, %cr4 + +	movl	%cr3, %eax +	movl	%eax, %cr3 + +	movl	$MSR_EFER, %ecx +	rdmsr +	btsl	$_EFER_LME, %eax +	wrmsr + +	xorl	%eax, %eax +	lldt	%ax + +	movl	72(%esp), %eax +	pushl	$__KERNEL_CS +	pushl	%eax + +	/* Enable paging */ +	movl	%cr0, %eax +	btsl	$X86_CR0_PG_BIT, %eax +	movl	%eax, %cr0 +	lret +ENDPROC(efi_enter32) + +	.data +	.balign	8 +	.global	efi32_boot_gdt +efi32_boot_gdt:	.word	0 +		.quad	0 + +save_gdt:	.word	0 +		.quad	0 +func_rt_ptr:	.quad	0 + +	.global efi_gdt64 +efi_gdt64: +	.word	efi_gdt64_end - efi_gdt64 +	.long	0			/* Filled out by user */ +	.word	0 +	.quad	0x0000000000000000	/* NULL descriptor */ +	.quad	0x00af9a000000ffff	/* __KERNEL_CS */ +	.quad	0x00cf92000000ffff	/* __KERNEL_DS */ +	.quad	0x0080890000000000	/* TS descriptor */ +	.quad   0x0000000000000000	/* TS continued */ +efi_gdt64_end: +#endif /* CONFIG_EFI_MIXED */ + +	.data +ENTRY(efi_scratch) +	.fill 3,8,0 +	.byte 0 +	.quad 0 diff --git a/arch/x86/platform/efi/efi_thunk_64.S b/arch/x86/platform/efi/efi_thunk_64.S new file mode 100644 index 00000000000..8806fa73e6e --- /dev/null +++ b/arch/x86/platform/efi/efi_thunk_64.S @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2014 Intel Corporation; author Matt Fleming + */ + +#include <linux/linkage.h> +#include <asm/page_types.h> + +	.text +	.code64 +ENTRY(efi64_thunk) +	push	%rbp +	push	%rbx + +	/* +	 * Switch to 1:1 mapped 32-bit stack pointer. +	 */ +	movq	%rsp, efi_saved_sp(%rip) +	movq	efi_scratch+25(%rip), %rsp + +	/* +	 * Calculate the physical address of the kernel text. +	 */ +	movq	$__START_KERNEL_map, %rax +	subq	phys_base(%rip), %rax + +	/* +	 * Push some physical addresses onto the stack. This is easier +	 * to do now in a code64 section while the assembler can address +	 * 64-bit values. Note that all the addresses on the stack are +	 * 32-bit. +	 */ +	subq	$16, %rsp +	leaq	efi_exit32(%rip), %rbx +	subq	%rax, %rbx +	movl	%ebx, 8(%rsp) +	leaq	efi_gdt64(%rip), %rbx +	subq	%rax, %rbx +	movl	%ebx, 2(%ebx) +	movl	%ebx, 4(%rsp) +	leaq	efi_gdt32(%rip), %rbx +	subq	%rax, %rbx +	movl	%ebx, 2(%ebx) +	movl	%ebx, (%rsp) + +	leaq	__efi64_thunk(%rip), %rbx +	subq	%rax, %rbx +	call	*%rbx + +	movq	efi_saved_sp(%rip), %rsp +	pop	%rbx +	pop	%rbp +	retq +ENDPROC(efi64_thunk) + +	.data +efi_gdt32: +	.word 	efi_gdt32_end - efi_gdt32 +	.long	0			/* Filled out above */ +	.word	0 +	.quad	0x0000000000000000	/* NULL descriptor */ +	.quad	0x00cf9a000000ffff	/* __KERNEL_CS */ +	.quad	0x00cf93000000ffff	/* __KERNEL_DS */ +efi_gdt32_end: + +efi_saved_sp:		.quad 0 diff --git a/arch/x86/platform/geode/alix.c b/arch/x86/platform/geode/alix.c index 90e23e7679a..76b6632d314 100644 --- a/arch/x86/platform/geode/alix.c +++ b/arch/x86/platform/geode/alix.c @@ -98,7 +98,7 @@ static struct platform_device alix_leds_dev = {  	.dev.platform_data = &alix_leds_data,  }; -static struct __initdata platform_device *alix_devs[] = { +static struct platform_device *alix_devs[] __initdata = {  	&alix_buttons_dev,  	&alix_leds_dev,  }; diff --git a/arch/x86/platform/geode/geos.c b/arch/x86/platform/geode/geos.c index c2e6d53558b..aa733fba247 100644 --- a/arch/x86/platform/geode/geos.c +++ b/arch/x86/platform/geode/geos.c @@ -87,7 +87,7 @@ static struct platform_device geos_leds_dev = {  	.dev.platform_data = &geos_leds_data,  }; -static struct __initdata platform_device *geos_devs[] = { +static struct platform_device *geos_devs[] __initdata = {  	&geos_buttons_dev,  	&geos_leds_dev,  }; diff --git a/arch/x86/platform/geode/net5501.c b/arch/x86/platform/geode/net5501.c index 646e3b5b4bb..927e38c0089 100644 --- a/arch/x86/platform/geode/net5501.c +++ b/arch/x86/platform/geode/net5501.c @@ -78,7 +78,7 @@ static struct platform_device net5501_leds_dev = {  	.dev.platform_data = &net5501_leds_data,  }; -static struct __initdata platform_device *net5501_devs[] = { +static struct platform_device *net5501_devs[] __initdata = {  	&net5501_buttons_dev,  	&net5501_leds_dev,  }; diff --git a/arch/x86/platform/intel-mid/Makefile b/arch/x86/platform/intel-mid/Makefile new file mode 100644 index 00000000000..0a8ee703b9f --- /dev/null +++ b/arch/x86/platform/intel-mid/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_X86_INTEL_MID) += intel-mid.o intel_mid_vrtc.o mfld.o mrfl.o +obj-$(CONFIG_EARLY_PRINTK_INTEL_MID) += early_printk_intel_mid.o + +# SFI specific code +ifdef CONFIG_X86_INTEL_MID +obj-$(CONFIG_SFI) += sfi.o device_libs/ +endif diff --git a/arch/x86/platform/intel-mid/device_libs/Makefile b/arch/x86/platform/intel-mid/device_libs/Makefile new file mode 100644 index 00000000000..af9307f2cc2 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/Makefile @@ -0,0 +1,23 @@ +# IPC Devices +obj-y += platform_ipc.o +obj-$(subst m,y,$(CONFIG_MFD_INTEL_MSIC)) += platform_msic.o +obj-$(subst m,y,$(CONFIG_SND_MFLD_MACHINE)) += platform_msic_audio.o +obj-$(subst m,y,$(CONFIG_GPIO_MSIC)) += platform_msic_gpio.o +obj-$(subst m,y,$(CONFIG_MFD_INTEL_MSIC)) += platform_msic_ocd.o +obj-$(subst m,y,$(CONFIG_MFD_INTEL_MSIC)) += platform_msic_battery.o +obj-$(subst m,y,$(CONFIG_INTEL_MID_POWER_BUTTON)) += platform_msic_power_btn.o +obj-$(subst m,y,$(CONFIG_GPIO_INTEL_PMIC)) += platform_pmic_gpio.o +obj-$(subst m,y,$(CONFIG_INTEL_MFLD_THERMAL)) += platform_msic_thermal.o +# I2C Devices +obj-$(subst m,y,$(CONFIG_SENSORS_EMC1403)) += platform_emc1403.o +obj-$(subst m,y,$(CONFIG_SENSORS_LIS3LV02D)) += platform_lis331.o +obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_max7315.o +obj-$(subst m,y,$(CONFIG_INPUT_MPU3050)) += platform_mpu3050.o +obj-$(subst m,y,$(CONFIG_INPUT_BMA150)) += platform_bma023.o +obj-$(subst m,y,$(CONFIG_GPIO_PCA953X)) += platform_tca6416.o +obj-$(subst m,y,$(CONFIG_DRM_MEDFIELD)) += platform_tc35876x.o +# SPI Devices +obj-$(subst m,y,$(CONFIG_SERIAL_MRST_MAX3110)) += platform_max3111.o +# MISC Devices +obj-$(subst m,y,$(CONFIG_KEYBOARD_GPIO)) += platform_gpio_keys.o +obj-$(subst m,y,$(CONFIG_INTEL_MID_WATCHDOG)) += platform_wdt.o diff --git a/arch/x86/platform/intel-mid/device_libs/platform_bma023.c b/arch/x86/platform/intel-mid/device_libs/platform_bma023.c new file mode 100644 index 00000000000..0ae7f2ae229 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_bma023.c @@ -0,0 +1,20 @@ +/* + * platform_bma023.c: bma023 platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <asm/intel-mid.h> + +static const struct devs_id bma023_dev_id __initconst = { +	.name = "bma023", +	.type = SFI_DEV_TYPE_I2C, +	.delay = 1, +}; + +sfi_device(bma023_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_emc1403.c b/arch/x86/platform/intel-mid/device_libs/platform_emc1403.c new file mode 100644 index 00000000000..69a783689d2 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_emc1403.c @@ -0,0 +1,43 @@ +/* + * platform_emc1403.c: emc1403 platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/init.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <asm/intel-mid.h> + +static void __init *emc1403_platform_data(void *info) +{ +	static short intr2nd_pdata; +	struct i2c_board_info *i2c_info = info; +	int intr = get_gpio_by_name("thermal_int"); +	int intr2nd = get_gpio_by_name("thermal_alert"); + +	if (intr < 0) +		return NULL; +	if (intr2nd < 0) +		return NULL; + +	i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; +	intr2nd_pdata = intr2nd + INTEL_MID_IRQ_OFFSET; + +	return &intr2nd_pdata; +} + +static const struct devs_id emc1403_dev_id __initconst = { +	.name = "emc1403", +	.type = SFI_DEV_TYPE_I2C, +	.delay = 1, +	.get_platform_data = &emc1403_platform_data, +}; + +sfi_device(emc1403_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_gpio_keys.c b/arch/x86/platform/intel-mid/device_libs/platform_gpio_keys.c new file mode 100644 index 00000000000..dccae6b0413 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_gpio_keys.c @@ -0,0 +1,83 @@ +/* + * platform_gpio_keys.c: gpio_keys platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/input.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/gpio.h> +#include <linux/gpio_keys.h> +#include <linux/platform_device.h> +#include <asm/intel-mid.h> + +#define DEVICE_NAME "gpio-keys" + +/* + * we will search these buttons in SFI GPIO table (by name) + * and register them dynamically. Please add all possible + * buttons here, we will shrink them if no GPIO found. + */ +static struct gpio_keys_button gpio_button[] = { +	{KEY_POWER,		-1, 1, "power_btn",	EV_KEY, 0, 3000}, +	{KEY_PROG1,		-1, 1, "prog_btn1",	EV_KEY, 0, 20}, +	{KEY_PROG2,		-1, 1, "prog_btn2",	EV_KEY, 0, 20}, +	{SW_LID,		-1, 1, "lid_switch",	EV_SW,  0, 20}, +	{KEY_VOLUMEUP,		-1, 1, "vol_up",	EV_KEY, 0, 20}, +	{KEY_VOLUMEDOWN,	-1, 1, "vol_down",	EV_KEY, 0, 20}, +	{KEY_CAMERA,		-1, 1, "camera_full",	EV_KEY, 0, 20}, +	{KEY_CAMERA_FOCUS,	-1, 1, "camera_half",	EV_KEY, 0, 20}, +	{SW_KEYPAD_SLIDE,	-1, 1, "MagSw1",	EV_SW,  0, 20}, +	{SW_KEYPAD_SLIDE,	-1, 1, "MagSw2",	EV_SW,  0, 20}, +}; + +static struct gpio_keys_platform_data gpio_keys = { +	.buttons	= gpio_button, +	.rep		= 1, +	.nbuttons	= -1, /* will fill it after search */ +}; + +static struct platform_device pb_device = { +	.name		= DEVICE_NAME, +	.id		= -1, +	.dev		= { +		.platform_data	= &gpio_keys, +	}, +}; + +/* + * Shrink the non-existent buttons, register the gpio button + * device if there is some + */ +static int __init pb_keys_init(void) +{ +	struct gpio_keys_button *gb = gpio_button; +	int i, num, good = 0; + +	num = sizeof(gpio_button) / sizeof(struct gpio_keys_button); +	for (i = 0; i < num; i++) { +		gb[i].gpio = get_gpio_by_name(gb[i].desc); +		pr_debug("info[%2d]: name = %s, gpio = %d\n", i, gb[i].desc, +					gb[i].gpio); +		if (gb[i].gpio < 0) +			continue; + +		if (i != good) +			gb[good] = gb[i]; +		good++; +	} + +	if (good) { +		gpio_keys.nbuttons = good; +		return platform_device_register(&pb_device); +	} +	return 0; +} +late_initcall(pb_keys_init); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_ipc.c b/arch/x86/platform/intel-mid/device_libs/platform_ipc.c new file mode 100644 index 00000000000..a84b73d6c4a --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_ipc.c @@ -0,0 +1,68 @@ +/* + * platform_ipc.c: IPC platform library file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/sfi.h> +#include <linux/gpio.h> +#include <asm/intel-mid.h> +#include "platform_ipc.h" + +void __init ipc_device_handler(struct sfi_device_table_entry *pentry, +				struct devs_id *dev) +{ +	struct platform_device *pdev; +	void *pdata = NULL; +	static struct resource res __initdata = { +		.name = "IRQ", +		.flags = IORESOURCE_IRQ, +	}; + +	pr_debug("IPC bus, name = %16.16s, irq = 0x%2x\n", +		pentry->name, pentry->irq); + +	/* +	 * We need to call platform init of IPC devices to fill misc_pdata +	 * structure. It will be used in msic_init for initialization. +	 */ +	if (dev != NULL) +		pdata = dev->get_platform_data(pentry); + +	/* +	 * On Medfield the platform device creation is handled by the MSIC +	 * MFD driver so we don't need to do it here. +	 */ +	if (intel_mid_has_msic()) +		return; + +	pdev = platform_device_alloc(pentry->name, 0); +	if (pdev == NULL) { +		pr_err("out of memory for SFI platform device '%s'.\n", +			pentry->name); +		return; +	} +	res.start = pentry->irq; +	platform_device_add_resources(pdev, &res, 1); + +	pdev->dev.platform_data = pdata; +	intel_scu_device_register(pdev); +} + +static const struct devs_id pmic_audio_dev_id __initconst = { +	.name = "pmic_audio", +	.type = SFI_DEV_TYPE_IPC, +	.delay = 1, +	.device_handler = &ipc_device_handler, +}; + +sfi_device(pmic_audio_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_ipc.h b/arch/x86/platform/intel-mid/device_libs/platform_ipc.h new file mode 100644 index 00000000000..79bb09d4f71 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_ipc.h @@ -0,0 +1,18 @@ +/* + * platform_ipc.h: IPC platform library header file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ +#ifndef _PLATFORM_IPC_H_ +#define _PLATFORM_IPC_H_ + +void __init +ipc_device_handler(struct sfi_device_table_entry *pentry, struct devs_id *dev); + +#endif diff --git a/arch/x86/platform/intel-mid/device_libs/platform_lis331.c b/arch/x86/platform/intel-mid/device_libs/platform_lis331.c new file mode 100644 index 00000000000..54226de7541 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_lis331.c @@ -0,0 +1,41 @@ +/* + * platform_lis331.c:  lis331 platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <asm/intel-mid.h> + +static void __init *lis331dl_platform_data(void *info) +{ +	static short intr2nd_pdata; +	struct i2c_board_info *i2c_info = info; +	int intr = get_gpio_by_name("accel_int"); +	int intr2nd = get_gpio_by_name("accel_2"); + +	if (intr < 0) +		return NULL; +	if (intr2nd < 0) +		return NULL; + +	i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; +	intr2nd_pdata = intr2nd + INTEL_MID_IRQ_OFFSET; + +	return &intr2nd_pdata; +} + +static const struct devs_id lis331dl_dev_id __initconst = { +	.name = "i2c_accel", +	.type = SFI_DEV_TYPE_I2C, +	.get_platform_data = &lis331dl_platform_data, +}; + +sfi_device(lis331dl_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_max3111.c b/arch/x86/platform/intel-mid/device_libs/platform_max3111.c new file mode 100644 index 00000000000..afd1df94e0e --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_max3111.c @@ -0,0 +1,35 @@ +/* + * platform_max3111.c: max3111 platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/gpio.h> +#include <linux/spi/spi.h> +#include <asm/intel-mid.h> + +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 + INTEL_MID_IRQ_OFFSET; +	return NULL; +} + +static const struct devs_id max3111_dev_id __initconst = { +	.name = "spi_max3111", +	.type = SFI_DEV_TYPE_SPI, +	.get_platform_data = &max3111_platform_data, +}; + +sfi_device(max3111_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_max7315.c b/arch/x86/platform/intel-mid/device_libs/platform_max7315.c new file mode 100644 index 00000000000..2c8acbc1e9a --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_max7315.c @@ -0,0 +1,79 @@ +/* + * platform_max7315.c: max7315 platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/init.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/platform_data/pca953x.h> +#include <asm/intel-mid.h> + +#define MAX7315_NUM 2 + +static void __init *max7315_platform_data(void *info) +{ +	static struct pca953x_platform_data max7315_pdata[MAX7315_NUM]; +	static int nr; +	struct pca953x_platform_data *max7315 = &max7315_pdata[nr]; +	struct i2c_board_info *i2c_info = info; +	int gpio_base, intr; +	char base_pin_name[SFI_NAME_LEN + 1]; +	char intr_pin_name[SFI_NAME_LEN + 1]; + +	if (nr == MAX7315_NUM) { +		pr_err("too many max7315s, we only support %d\n", +				MAX7315_NUM); +		return NULL; +	} +	/* we have several max7315 on the board, we only need load several +	 * instances of the same pca953x driver to cover them +	 */ +	strcpy(i2c_info->type, "max7315"); +	if (nr++) { +		sprintf(base_pin_name, "max7315_%d_base", nr); +		sprintf(intr_pin_name, "max7315_%d_int", nr); +	} else { +		strcpy(base_pin_name, "max7315_base"); +		strcpy(intr_pin_name, "max7315_int"); +	} + +	gpio_base = get_gpio_by_name(base_pin_name); +	intr = get_gpio_by_name(intr_pin_name); + +	if (gpio_base < 0) +		return NULL; +	max7315->gpio_base = gpio_base; +	if (intr != -1) { +		i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; +		max7315->irq_base = gpio_base + INTEL_MID_IRQ_OFFSET; +	} else { +		i2c_info->irq = -1; +		max7315->irq_base = -1; +	} +	return max7315; +} + +static const struct devs_id max7315_dev_id __initconst = { +	.name = "i2c_max7315", +	.type = SFI_DEV_TYPE_I2C, +	.delay = 1, +	.get_platform_data = &max7315_platform_data, +}; + +static const struct devs_id max7315_2_dev_id __initconst = { +	.name = "i2c_max7315_2", +	.type = SFI_DEV_TYPE_I2C, +	.delay = 1, +	.get_platform_data = &max7315_platform_data, +}; + +sfi_device(max7315_dev_id); +sfi_device(max7315_2_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_mpu3050.c b/arch/x86/platform/intel-mid/device_libs/platform_mpu3050.c new file mode 100644 index 00000000000..cfe9a47a1e8 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_mpu3050.c @@ -0,0 +1,36 @@ +/* + * platform_mpu3050.c: mpu3050 platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <asm/intel-mid.h> + +static void *mpu3050_platform_data(void *info) +{ +	struct i2c_board_info *i2c_info = info; +	int intr = get_gpio_by_name("mpu3050_int"); + +	if (intr < 0) +		return NULL; + +	i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; +	return NULL; +} + +static const struct devs_id mpu3050_dev_id __initconst = { +	.name = "mpu3050", +	.type = SFI_DEV_TYPE_I2C, +	.delay = 1, +	.get_platform_data = &mpu3050_platform_data, +}; + +sfi_device(mpu3050_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic.c b/arch/x86/platform/intel-mid/device_libs/platform_msic.c new file mode 100644 index 00000000000..9f4a775a69d --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic.c @@ -0,0 +1,87 @@ +/* + * platform_msic.c: MSIC platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/scatterlist.h> +#include <linux/init.h> +#include <linux/sfi.h> +#include <linux/mfd/intel_msic.h> +#include <asm/intel_scu_ipc.h> +#include <asm/intel-mid.h> +#include "platform_msic.h" + +struct intel_msic_platform_data msic_pdata; + +static struct resource msic_resources[] = { +	{ +		.start	= INTEL_MSIC_IRQ_PHYS_BASE, +		.end	= INTEL_MSIC_IRQ_PHYS_BASE + 64 - 1, +		.flags	= IORESOURCE_MEM, +	}, +}; + +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 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 (intel_mid_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. + */ +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 NULL; +} diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic.h b/arch/x86/platform/intel-mid/device_libs/platform_msic.h new file mode 100644 index 00000000000..b7be1d041da --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic.h @@ -0,0 +1,19 @@ +/* + * platform_msic.h: MSIC platform data header file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ +#ifndef _PLATFORM_MSIC_H_ +#define _PLATFORM_MSIC_H_ + +extern struct intel_msic_platform_data msic_pdata; + +void *msic_generic_platform_data(void *info, enum intel_msic_block block); + +#endif diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_audio.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_audio.c new file mode 100644 index 00000000000..29629397d2b --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_audio.c @@ -0,0 +1,47 @@ +/* + * platform_msic_audio.c: MSIC audio platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/scatterlist.h> +#include <linux/init.h> +#include <linux/sfi.h> +#include <linux/platform_device.h> +#include <linux/mfd/intel_msic.h> +#include <asm/intel-mid.h> + +#include "platform_msic.h" +#include "platform_ipc.h" + +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 const struct devs_id msic_audio_dev_id __initconst = { +	.name = "msic_audio", +	.type = SFI_DEV_TYPE_IPC, +	.delay = 1, +	.get_platform_data = &msic_audio_platform_data, +	.device_handler = &ipc_device_handler, +}; + +sfi_device(msic_audio_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_battery.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_battery.c new file mode 100644 index 00000000000..f446c33df1a --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_battery.c @@ -0,0 +1,37 @@ +/* + * platform_msic_battery.c: MSIC battery platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/scatterlist.h> +#include <linux/init.h> +#include <linux/sfi.h> +#include <linux/mfd/intel_msic.h> +#include <asm/intel-mid.h> + +#include "platform_msic.h" +#include "platform_ipc.h" + +static void __init *msic_battery_platform_data(void *info) +{ +	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_BATTERY); +} + +static const struct devs_id msic_battery_dev_id __initconst = { +	.name = "msic_battery", +	.type = SFI_DEV_TYPE_IPC, +	.delay = 1, +	.get_platform_data = &msic_battery_platform_data, +	.device_handler = &ipc_device_handler, +}; + +sfi_device(msic_battery_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_gpio.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_gpio.c new file mode 100644 index 00000000000..2a4f7b1dd91 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_gpio.c @@ -0,0 +1,48 @@ +/* + * platform_msic_gpio.c: MSIC GPIO platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/scatterlist.h> +#include <linux/sfi.h> +#include <linux/init.h> +#include <linux/gpio.h> +#include <linux/mfd/intel_msic.h> +#include <asm/intel-mid.h> + +#include "platform_msic.h" +#include "platform_ipc.h" + +static void __init *msic_gpio_platform_data(void *info) +{ +	static struct intel_msic_gpio_pdata msic_gpio_pdata; + +	int gpio = get_gpio_by_name("msic_gpio_base"); + +	if (gpio < 0) +		return NULL; + +	msic_gpio_pdata.gpio_base = gpio; +	msic_pdata.gpio = &msic_gpio_pdata; + +	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_GPIO); +} + +static const struct devs_id msic_gpio_dev_id __initconst = { +	.name = "msic_gpio", +	.type = SFI_DEV_TYPE_IPC, +	.delay = 1, +	.get_platform_data = &msic_gpio_platform_data, +	.device_handler = &ipc_device_handler, +}; + +sfi_device(msic_gpio_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_ocd.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_ocd.c new file mode 100644 index 00000000000..6497111ddb5 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_ocd.c @@ -0,0 +1,49 @@ +/* + * platform_msic_ocd.c: MSIC OCD platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/scatterlist.h> +#include <linux/sfi.h> +#include <linux/init.h> +#include <linux/gpio.h> +#include <linux/mfd/intel_msic.h> +#include <asm/intel-mid.h> + +#include "platform_msic.h" +#include "platform_ipc.h" + +static void __init *msic_ocd_platform_data(void *info) +{ +	static struct intel_msic_ocd_pdata msic_ocd_pdata; +	int gpio; + +	gpio = get_gpio_by_name("ocd_gpio"); + +	if (gpio < 0) +		return NULL; + +	msic_ocd_pdata.gpio = gpio; +	msic_pdata.ocd = &msic_ocd_pdata; + +	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_OCD); +} + +static const struct devs_id msic_ocd_dev_id __initconst = { +	.name = "msic_ocd", +	.type = SFI_DEV_TYPE_IPC, +	.delay = 1, +	.get_platform_data = &msic_ocd_platform_data, +	.device_handler = &ipc_device_handler, +}; + +sfi_device(msic_ocd_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_power_btn.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_power_btn.c new file mode 100644 index 00000000000..83a3459bc33 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_power_btn.c @@ -0,0 +1,36 @@ +/* + * platform_msic_power_btn.c: MSIC power btn platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/scatterlist.h> +#include <linux/sfi.h> +#include <linux/init.h> +#include <linux/mfd/intel_msic.h> +#include <asm/intel-mid.h> + +#include "platform_msic.h" +#include "platform_ipc.h" + +static void __init *msic_power_btn_platform_data(void *info) +{ +	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_POWER_BTN); +} + +static const struct devs_id msic_power_btn_dev_id __initconst = { +	.name = "msic_power_btn", +	.type = SFI_DEV_TYPE_IPC, +	.delay = 1, +	.get_platform_data = &msic_power_btn_platform_data, +	.device_handler = &ipc_device_handler, +}; + +sfi_device(msic_power_btn_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_msic_thermal.c b/arch/x86/platform/intel-mid/device_libs/platform_msic_thermal.c new file mode 100644 index 00000000000..a351878b96b --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_msic_thermal.c @@ -0,0 +1,37 @@ +/* + * platform_msic_thermal.c: msic_thermal platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/input.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/gpio.h> +#include <linux/platform_device.h> +#include <linux/mfd/intel_msic.h> +#include <asm/intel-mid.h> + +#include "platform_msic.h" +#include "platform_ipc.h" + +static void __init *msic_thermal_platform_data(void *info) +{ +	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_THERMAL); +} + +static const struct devs_id msic_thermal_dev_id __initconst = { +	.name = "msic_thermal", +	.type = SFI_DEV_TYPE_IPC, +	.delay = 1, +	.get_platform_data = &msic_thermal_platform_data, +	.device_handler = &ipc_device_handler, +}; + +sfi_device(msic_thermal_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_pmic_gpio.c b/arch/x86/platform/intel-mid/device_libs/platform_pmic_gpio.c new file mode 100644 index 00000000000..65c2a9a19db --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_pmic_gpio.c @@ -0,0 +1,54 @@ +/* + * platform_pmic_gpio.c: PMIC GPIO platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/scatterlist.h> +#include <linux/gpio.h> +#include <linux/init.h> +#include <linux/sfi.h> +#include <linux/intel_pmic_gpio.h> +#include <asm/intel-mid.h> + +#include "platform_ipc.h" + +static void __init *pmic_gpio_platform_data(void *info) +{ +	static struct intel_pmic_gpio_platform_data pmic_gpio_pdata; +	int gpio_base = get_gpio_by_name("pmic_gpio_base"); + +	if (gpio_base < 0) +		gpio_base = 64; +	pmic_gpio_pdata.gpio_base = gpio_base; +	pmic_gpio_pdata.irq_base = gpio_base + INTEL_MID_IRQ_OFFSET; +	pmic_gpio_pdata.gpiointr = 0xffffeff8; + +	return &pmic_gpio_pdata; +} + +static const struct devs_id pmic_gpio_spi_dev_id __initconst = { +	.name = "pmic_gpio", +	.type = SFI_DEV_TYPE_SPI, +	.delay = 1, +	.get_platform_data = &pmic_gpio_platform_data, +}; + +static const struct devs_id pmic_gpio_ipc_dev_id __initconst = { +	.name = "pmic_gpio", +	.type = SFI_DEV_TYPE_IPC, +	.delay = 1, +	.get_platform_data = &pmic_gpio_platform_data, +	.device_handler = &ipc_device_handler +}; + +sfi_device(pmic_gpio_spi_dev_id); +sfi_device(pmic_gpio_ipc_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_tc35876x.c b/arch/x86/platform/intel-mid/device_libs/platform_tc35876x.c new file mode 100644 index 00000000000..740fc757050 --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_tc35876x.c @@ -0,0 +1,36 @@ +/* + * platform_tc35876x.c: tc35876x platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/gpio.h> +#include <linux/i2c/tc35876x.h> +#include <asm/intel-mid.h> + +/*tc35876x DSI_LVDS bridge chip and panel platform data*/ +static void *tc35876x_platform_data(void *data) +{ +	static struct tc35876x_platform_data pdata; + +	/* gpio pins set to -1 will not be used by the driver */ +	pdata.gpio_bridge_reset = get_gpio_by_name("LCMB_RXEN"); +	pdata.gpio_panel_bl_en = get_gpio_by_name("6S6P_BL_EN"); +	pdata.gpio_panel_vadd = get_gpio_by_name("EN_VREG_LCD_V3P3"); + +	return &pdata; +} + +static const struct devs_id tc35876x_dev_id __initconst = { +	.name = "i2c_disp_brig", +	.type = SFI_DEV_TYPE_I2C, +	.get_platform_data = &tc35876x_platform_data, +}; + +sfi_device(tc35876x_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_tca6416.c b/arch/x86/platform/intel-mid/device_libs/platform_tca6416.c new file mode 100644 index 00000000000..33be0b3be6e --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_tca6416.c @@ -0,0 +1,57 @@ +/* + * platform_tca6416.c: tca6416 platform data initilization file + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/platform_data/pca953x.h> +#include <linux/i2c.h> +#include <linux/gpio.h> +#include <asm/intel-mid.h> + +#define TCA6416_NAME	"tca6416" +#define TCA6416_BASE	"tca6416_base" +#define TCA6416_INTR	"tca6416_int" + +static void *tca6416_platform_data(void *info) +{ +	static struct pca953x_platform_data tca6416; +	struct i2c_board_info *i2c_info = info; +	int gpio_base, intr; +	char base_pin_name[SFI_NAME_LEN + 1]; +	char intr_pin_name[SFI_NAME_LEN + 1]; + +	strcpy(i2c_info->type, TCA6416_NAME); +	strcpy(base_pin_name, TCA6416_BASE); +	strcpy(intr_pin_name, TCA6416_INTR); + +	gpio_base = get_gpio_by_name(base_pin_name); +	intr = get_gpio_by_name(intr_pin_name); + +	if (gpio_base < 0) +		return NULL; +	tca6416.gpio_base = gpio_base; +	if (intr >= 0) { +		i2c_info->irq = intr + INTEL_MID_IRQ_OFFSET; +		tca6416.irq_base = gpio_base + INTEL_MID_IRQ_OFFSET; +	} else { +		i2c_info->irq = -1; +		tca6416.irq_base = -1; +	} +	return &tca6416; +} + +static const struct devs_id tca6416_dev_id __initconst = { +	.name = "tca6416", +	.type = SFI_DEV_TYPE_I2C, +	.delay = 1, +	.get_platform_data = &tca6416_platform_data, +}; + +sfi_device(tca6416_dev_id); diff --git a/arch/x86/platform/intel-mid/device_libs/platform_wdt.c b/arch/x86/platform/intel-mid/device_libs/platform_wdt.c new file mode 100644 index 00000000000..973cf3bfa9f --- /dev/null +++ b/arch/x86/platform/intel-mid/device_libs/platform_wdt.c @@ -0,0 +1,72 @@ +/* + * platform_wdt.c: Watchdog platform library file + * + * (C) Copyright 2014 Intel Corporation + * Author: David Cohen <david.a.cohen@linux.intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/platform_data/intel-mid_wdt.h> +#include <asm/intel-mid.h> +#include <asm/io_apic.h> + +#define TANGIER_EXT_TIMER0_MSI 15 + +static struct platform_device wdt_dev = { +	.name = "intel_mid_wdt", +	.id = -1, +}; + +static int tangier_probe(struct platform_device *pdev) +{ +	int ioapic; +	int irq; +	struct intel_mid_wdt_pdata *pdata = pdev->dev.platform_data; +	struct io_apic_irq_attr irq_attr = { 0 }; + +	if (!pdata) +		return -EINVAL; + +	irq = pdata->irq; +	ioapic = mp_find_ioapic(irq); +	if (ioapic >= 0) { +		int ret; +		irq_attr.ioapic = ioapic; +		irq_attr.ioapic_pin = irq; +		irq_attr.trigger = 1; +		/* irq_attr.polarity = 0; -> Active high */ +		ret = io_apic_set_pci_routing(NULL, irq, &irq_attr); +		if (ret) +			return ret; +	} else { +		dev_warn(&pdev->dev, "cannot find interrupt %d in ioapic\n", +			 irq); +		return -EINVAL; +	} + +	return 0; +} + +static struct intel_mid_wdt_pdata tangier_pdata = { +	.irq = TANGIER_EXT_TIMER0_MSI, +	.probe = tangier_probe, +}; + +static int __init register_mid_wdt(void) +{ +	if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_TANGIER) { +		wdt_dev.dev.platform_data = &tangier_pdata; +		return platform_device_register(&wdt_dev); +	} + +	return -ENODEV; +} + +rootfs_initcall(register_mid_wdt); diff --git a/arch/x86/platform/mrst/early_printk_mrst.c b/arch/x86/platform/intel-mid/early_printk_intel_mid.c index 028454f0c3a..e0bd082a80e 100644 --- a/arch/x86/platform/mrst/early_printk_mrst.c +++ b/arch/x86/platform/intel-mid/early_printk_intel_mid.c @@ -1,5 +1,5 @@  /* - * early_printk_mrst.c - early consoles for Intel MID platforms + * early_printk_intel_mid.c - early consoles for Intel MID platforms   *   * Copyright (c) 2008-2010, Intel Corporation   * @@ -22,12 +22,11 @@  #include <linux/console.h>  #include <linux/kernel.h>  #include <linux/delay.h> -#include <linux/init.h>  #include <linux/io.h>  #include <asm/fixmap.h>  #include <asm/pgtable.h> -#include <asm/mrst.h> +#include <asm/intel-mid.h>  #define MRST_SPI_TIMEOUT		0x200000  #define MRST_REGBASE_SPI0		0xff128000 @@ -152,7 +151,7 @@ void mrst_early_console_init(void)  	spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9;  	freq = 100000000 / (spi0_cdiv + 1); -	if (mrst_identify_cpu() == MRST_CPU_CHIP_PENWELL) +	if (intel_mid_identify_cpu() == INTEL_MID_CPU_CHIP_PENWELL)  		mrst_spi_paddr = MRST_REGBASE_SPI1;  	pspi = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, @@ -213,13 +212,14 @@ static void early_mrst_spi_putc(char c)  	}  	if (!timeout) -		pr_warning("MRST earlycon: timed out\n"); +		pr_warn("MRST earlycon: timed out\n");  	else  		max3110_write_data(c);  }  /* Early SPI only uses polling mode */ -static void early_mrst_spi_write(struct console *con, const char *str, unsigned n) +static void early_mrst_spi_write(struct console *con, const char *str, +					unsigned n)  {  	int i; diff --git a/arch/x86/platform/intel-mid/intel-mid.c b/arch/x86/platform/intel-mid/intel-mid.c new file mode 100644 index 00000000000..1bbedc4b0f8 --- /dev/null +++ b/arch/x86/platform/intel-mid/intel-mid.c @@ -0,0 +1,217 @@ +/* + * intel-mid.c: Intel MID platform setup code + * + * (C) Copyright 2008, 2012 Intel Corporation + * Author: Jacob Pan (jacob.jun.pan@intel.com) + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#define pr_fmt(fmt) "intel_mid: " fmt + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/scatterlist.h> +#include <linux/sfi.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/notifier.h> + +#include <asm/setup.h> +#include <asm/mpspec_def.h> +#include <asm/hw_irq.h> +#include <asm/apic.h> +#include <asm/io_apic.h> +#include <asm/intel-mid.h> +#include <asm/intel_mid_vrtc.h> +#include <asm/io.h> +#include <asm/i8259.h> +#include <asm/intel_scu_ipc.h> +#include <asm/apb_timer.h> +#include <asm/reboot.h> + +#include "intel_mid_weak_decls.h" + +/* + * the clockevent devices on Moorestown/Medfield can be APBT or LAPIC clock, + * cmdline option x86_intel_mid_timer can be used to override the configuration + * to prefer one or the other. + * at runtime, there are basically three timer configurations: + * 1. per cpu apbt clock only + * 2. per cpu always-on lapic clocks only, this is Penwell/Medfield only + * 3. per cpu lapic clock (C3STOP) and one apbt clock, with broadcast. + * + * by default (without cmdline option), platform code first detects cpu type + * to see if we are on lincroft or penwell, then set up both lapic or apbt + * clocks accordingly. + * i.e. by default, medfield uses configuration #2, moorestown uses #1. + * config #3 is supported but not recommended on medfield. + * + * rating and feature summary: + * lapic (with C3STOP) --------- 100 + * apbt (always-on) ------------ 110 + * lapic (always-on,ARAT) ------ 150 + */ + +enum intel_mid_timer_options intel_mid_timer_options; + +/* intel_mid_ops to store sub arch ops */ +struct intel_mid_ops *intel_mid_ops; +/* getter function for sub arch ops*/ +static void *(*get_intel_mid_ops[])(void) = INTEL_MID_OPS_INIT; +enum intel_mid_cpu_type __intel_mid_cpu_chip; +EXPORT_SYMBOL_GPL(__intel_mid_cpu_chip); + +static void intel_mid_power_off(void) +{ +}; + +static void intel_mid_reboot(void) +{ +	intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0); +} + +static unsigned long __init intel_mid_calibrate_tsc(void) +{ +	return 0; +} + +static void __init intel_mid_time_init(void) +{ +	sfi_table_parse(SFI_SIG_MTMR, NULL, NULL, sfi_parse_mtmr); +	switch (intel_mid_timer_options) { +	case INTEL_MID_TIMER_APBT_ONLY: +		break; +	case INTEL_MID_TIMER_LAPIC_APBT: +		x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; +		x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; +		break; +	default: +		if (!boot_cpu_has(X86_FEATURE_ARAT)) +			break; +		x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; +		x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; +		return; +	} +	/* we need at least one APB timer */ +	pre_init_apic_IRQ0(); +	apbt_time_init(); +} + +static void intel_mid_arch_setup(void) +{ +	if (boot_cpu_data.x86 != 6) { +		pr_err("Unknown Intel MID CPU (%d:%d), default to Penwell\n", +			boot_cpu_data.x86, boot_cpu_data.x86_model); +		__intel_mid_cpu_chip = INTEL_MID_CPU_CHIP_PENWELL; +		goto out; +	} + +	switch (boot_cpu_data.x86_model) { +	case 0x35: +		__intel_mid_cpu_chip = INTEL_MID_CPU_CHIP_CLOVERVIEW; +		break; +	case 0x3C: +	case 0x4A: +		__intel_mid_cpu_chip = INTEL_MID_CPU_CHIP_TANGIER; +		break; +	case 0x27: +	default: +		__intel_mid_cpu_chip = INTEL_MID_CPU_CHIP_PENWELL; +		break; +	} + +	if (__intel_mid_cpu_chip < MAX_CPU_OPS(get_intel_mid_ops)) +		intel_mid_ops = get_intel_mid_ops[__intel_mid_cpu_chip](); +	else { +		intel_mid_ops = get_intel_mid_ops[INTEL_MID_CPU_CHIP_PENWELL](); +		pr_info("ARCH: Uknown SoC, assuming PENWELL!\n"); +	} + +out: +	if (intel_mid_ops->arch_setup) +		intel_mid_ops->arch_setup(); +} + +/* MID systems don't have i8042 controller */ +static int intel_mid_i8042_detect(void) +{ +	return 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 intel_mid_get_nmi_reason(void) +{ +	return 0; +} + +/* + * Moorestown specific x86_init function overrides and early setup + * calls. + */ +void __init x86_intel_mid_early_setup(void) +{ +	x86_init.resources.probe_roms = x86_init_noop; +	x86_init.resources.reserve_resources = x86_init_noop; + +	x86_init.timers.timer_init = intel_mid_time_init; +	x86_init.timers.setup_percpu_clockev = x86_init_noop; + +	x86_init.irqs.pre_vector_init = x86_init_noop; + +	x86_init.oem.arch_setup = intel_mid_arch_setup; + +	x86_cpuinit.setup_percpu_clockev = apbt_setup_secondary_clock; + +	x86_platform.calibrate_tsc = intel_mid_calibrate_tsc; +	x86_platform.i8042_detect = intel_mid_i8042_detect; +	x86_init.timers.wallclock_init = intel_mid_rtc_init; +	x86_platform.get_nmi_reason = intel_mid_get_nmi_reason; + +	x86_init.pci.init = intel_mid_pci_init; +	x86_init.pci.fixup_irqs = x86_init_noop; + +	legacy_pic = &null_legacy_pic; + +	pm_power_off = intel_mid_power_off; +	machine_ops.emergency_restart  = intel_mid_reboot; + +	/* Avoid searching for BIOS MP tables */ +	x86_init.mpparse.find_smp_config = x86_init_noop; +	x86_init.mpparse.get_smp_config = x86_init_uint_noop; +	set_bit(MP_BUS_ISA, mp_bus_not_pci); +} + +/* + * if user does not want to use per CPU apb timer, just give it a lower rating + * than local apic timer and skip the late per cpu timer init. + */ +static inline int __init setup_x86_intel_mid_timer(char *arg) +{ +	if (!arg) +		return -EINVAL; + +	if (strcmp("apbt_only", arg) == 0) +		intel_mid_timer_options = INTEL_MID_TIMER_APBT_ONLY; +	else if (strcmp("lapic_and_apbt", arg) == 0) +		intel_mid_timer_options = INTEL_MID_TIMER_LAPIC_APBT; +	else { +		pr_warn("X86 INTEL_MID timer option %s not recognised" +			   " use x86_intel_mid_timer=apbt_only or lapic_and_apbt\n", +			   arg); +		return -EINVAL; +	} +	return 0; +} +__setup("x86_intel_mid_timer=", setup_x86_intel_mid_timer); + diff --git a/arch/x86/platform/mrst/vrtc.c b/arch/x86/platform/intel-mid/intel_mid_vrtc.c index 5e355b134ba..4762cff7fac 100644 --- a/arch/x86/platform/mrst/vrtc.c +++ b/arch/x86/platform/intel-mid/intel_mid_vrtc.c @@ -1,5 +1,5 @@  /* - * vrtc.c: Driver for virtual RTC device on Intel MID platform + * intel_mid_vrtc.c: Driver for virtual RTC device on Intel MID platform   *   * (C) Copyright 2009 Intel Corporation   * @@ -23,8 +23,8 @@  #include <linux/sfi.h>  #include <linux/platform_device.h> -#include <asm/mrst.h> -#include <asm/mrst-vrtc.h> +#include <asm/intel-mid.h> +#include <asm/intel_mid_vrtc.h>  #include <asm/time.h>  #include <asm/fixmap.h> @@ -79,7 +79,7 @@ void vrtc_get_time(struct timespec *now)  	/* vRTC YEAR reg contains the offset to 1972 */  	year += 1972; -	printk(KERN_INFO "vRTC: sec: %d min: %d hour: %d day: %d " +	pr_info("vRTC: sec: %d min: %d hour: %d day: %d "  		"mon: %d year: %d\n", sec, min, hour, mday, mon, year);  	now->tv_sec = mktime(year, mon, mday, hour, min, sec); @@ -109,15 +109,14 @@ int vrtc_set_mmss(const struct timespec *now)  		vrtc_cmos_write(tm.tm_sec, RTC_SECONDS);  		spin_unlock_irqrestore(&rtc_lock, flags);  	} else { -		printk(KERN_ERR -		       "%s: Invalid vRTC value: write of %lx to vRTC failed\n", +		pr_err("%s: Invalid vRTC value: write of %lx to vRTC failed\n",  			__FUNCTION__, now->tv_sec);  		retval = -EINVAL;  	}  	return retval;  } -void __init mrst_rtc_init(void) +void __init intel_mid_rtc_init(void)  {  	unsigned long vrtc_paddr; @@ -155,10 +154,10 @@ static struct platform_device vrtc_device = {  };  /* Register the RTC device if appropriate */ -static int __init mrst_device_create(void) +static int __init intel_mid_device_create(void)  {  	/* No Moorestown, no device */ -	if (!mrst_identify_cpu()) +	if (!intel_mid_identify_cpu())  		return -ENODEV;  	/* No timer, no device */  	if (!sfi_mrtc_num) @@ -175,4 +174,4 @@ static int __init mrst_device_create(void)  	return platform_device_register(&vrtc_device);  } -module_init(mrst_device_create); +module_init(intel_mid_device_create); diff --git a/arch/x86/platform/intel-mid/intel_mid_weak_decls.h b/arch/x86/platform/intel-mid/intel_mid_weak_decls.h new file mode 100644 index 00000000000..46aa25c8ce0 --- /dev/null +++ b/arch/x86/platform/intel-mid/intel_mid_weak_decls.h @@ -0,0 +1,19 @@ +/* + * intel_mid_weak_decls.h: Weak declarations of intel-mid.c + * + * (C) Copyright 2013 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + + +/* __attribute__((weak)) makes these declarations overridable */ +/* For every CPU addition a new get_<cpuname>_ops interface needs + * to be added. + */ +extern void *get_penwell_ops(void) __attribute__((weak)); +extern void *get_cloverview_ops(void) __attribute__((weak)); +extern void *get_tangier_ops(void) __attribute__((weak)); diff --git a/arch/x86/platform/intel-mid/mfld.c b/arch/x86/platform/intel-mid/mfld.c new file mode 100644 index 00000000000..23381d2174a --- /dev/null +++ b/arch/x86/platform/intel-mid/mfld.c @@ -0,0 +1,75 @@ +/* + * mfld.c: Intel Medfield platform setup code + * + * (C) Copyright 2013 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/init.h> + +#include <asm/apic.h> +#include <asm/intel-mid.h> +#include <asm/intel_mid_vrtc.h> + +#include "intel_mid_weak_decls.h" + +static void penwell_arch_setup(void); +/* penwell arch ops */ +static struct intel_mid_ops penwell_ops = { +	.arch_setup = penwell_arch_setup, +}; + +static void mfld_power_off(void) +{ +} + +static unsigned long __init mfld_calibrate_tsc(void) +{ +	unsigned long fast_calibrate; +	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 = FSB_FREQ_83SKU; +	else +		fsb = 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); + +	if (fast_calibrate) +		return fast_calibrate; + +	return 0; +} + +static void __init penwell_arch_setup(void) +{ +	x86_platform.calibrate_tsc = mfld_calibrate_tsc; +	pm_power_off = mfld_power_off; +} + +void *get_penwell_ops(void) +{ +	return &penwell_ops; +} + +void *get_cloverview_ops(void) +{ +	return &penwell_ops; +} diff --git a/arch/x86/platform/intel-mid/mrfl.c b/arch/x86/platform/intel-mid/mrfl.c new file mode 100644 index 00000000000..aaca91753d3 --- /dev/null +++ b/arch/x86/platform/intel-mid/mrfl.c @@ -0,0 +1,103 @@ +/* + * mrfl.c: Intel Merrifield platform specific setup code + * + * (C) Copyright 2013 Intel Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/init.h> + +#include <asm/apic.h> +#include <asm/intel-mid.h> + +#include "intel_mid_weak_decls.h" + +static unsigned long __init tangier_calibrate_tsc(void) +{ +	unsigned long fast_calibrate; +	u32 lo, hi, ratio, fsb, bus_freq; + +	/* *********************** */ +	/* Compute TSC:Ratio * FSB */ +	/* *********************** */ + +	/* Compute Ratio */ +	rdmsr(MSR_PLATFORM_INFO, lo, hi); +	pr_debug("IA32 PLATFORM_INFO is 0x%x : %x\n", hi, lo); + +	ratio = (lo >> 8) & 0xFF; +	pr_debug("ratio is %d\n", ratio); +	if (!ratio) { +		pr_err("Read a zero ratio, force tsc ratio to 4 ...\n"); +		ratio = 4; +	} + +	/* Compute FSB */ +	rdmsr(MSR_FSB_FREQ, lo, hi); +	pr_debug("Actual FSB frequency detected by SOC 0x%x : %x\n", +			hi, lo); + +	bus_freq = lo & 0x7; +	pr_debug("bus_freq = 0x%x\n", bus_freq); + +	if (bus_freq == 0) +		fsb = FSB_FREQ_100SKU; +	else if (bus_freq == 1) +		fsb = FSB_FREQ_100SKU; +	else if (bus_freq == 2) +		fsb = FSB_FREQ_133SKU; +	else if (bus_freq == 3) +		fsb = FSB_FREQ_167SKU; +	else if (bus_freq == 4) +		fsb = FSB_FREQ_83SKU; +	else if (bus_freq == 5) +		fsb = FSB_FREQ_400SKU; +	else if (bus_freq == 6) +		fsb = FSB_FREQ_267SKU; +	else if (bus_freq == 7) +		fsb = FSB_FREQ_333SKU; +	else { +		BUG(); +		pr_err("Invalid bus_freq! Setting to minimal value!\n"); +		fsb = FSB_FREQ_100SKU; +	} + +	/* TSC = FSB Freq * Resolved HFM Ratio */ +	fast_calibrate = ratio * fsb; +	pr_debug("calculate tangier tsc %lu KHz\n", fast_calibrate); + +	/* ************************************ */ +	/* Calculate Local APIC Timer Frequency */ +	/* ************************************ */ +	lapic_timer_frequency = (fsb * 1000) / HZ; + +	pr_debug("Setting lapic_timer_frequency = %d\n", +			lapic_timer_frequency); + +	/* mark tsc clocksource as reliable */ +	set_cpu_cap(&boot_cpu_data, X86_FEATURE_TSC_RELIABLE); + +	if (fast_calibrate) +		return fast_calibrate; + +	return 0; +} + +static void __init tangier_arch_setup(void) +{ +	x86_platform.calibrate_tsc = tangier_calibrate_tsc; +} + +/* tangier arch ops */ +static struct intel_mid_ops tangier_ops = { +	.arch_setup = tangier_arch_setup, +}; + +void *get_tangier_ops(void) +{ +	return &tangier_ops; +} diff --git a/arch/x86/platform/intel-mid/sfi.c b/arch/x86/platform/intel-mid/sfi.c new file mode 100644 index 00000000000..994c40bd7cb --- /dev/null +++ b/arch/x86/platform/intel-mid/sfi.c @@ -0,0 +1,516 @@ +/* + * intel_mid_sfi.c: Intel MID SFI initialization code + * + * (C) Copyright 2013 Intel Corporation + * Author: Sathyanarayanan Kuppuswamy <sathyanarayanan.kuppuswamy@intel.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; version 2 + * of the License. + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/scatterlist.h> +#include <linux/sfi.h> +#include <linux/intel_pmic_gpio.h> +#include <linux/spi/spi.h> +#include <linux/i2c.h> +#include <linux/skbuff.h> +#include <linux/gpio.h> +#include <linux/gpio_keys.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/notifier.h> +#include <linux/mmc/core.h> +#include <linux/mmc/card.h> +#include <linux/blkdev.h> + +#include <asm/setup.h> +#include <asm/mpspec_def.h> +#include <asm/hw_irq.h> +#include <asm/apic.h> +#include <asm/io_apic.h> +#include <asm/intel-mid.h> +#include <asm/intel_mid_vrtc.h> +#include <asm/io.h> +#include <asm/i8259.h> +#include <asm/intel_scu_ipc.h> +#include <asm/apb_timer.h> +#include <asm/reboot.h> + +#define	SFI_SIG_OEM0	"OEM0" +#define MAX_IPCDEVS	24 +#define MAX_SCU_SPI	24 +#define MAX_SCU_I2C	24 + +static struct platform_device *ipc_devs[MAX_IPCDEVS]; +static struct spi_board_info *spi_devs[MAX_SCU_SPI]; +static struct i2c_board_info *i2c_devs[MAX_SCU_I2C]; +static struct sfi_gpio_table_entry *gpio_table; +static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; +static int ipc_next_dev; +static int spi_next_dev; +static int i2c_next_dev; +static int i2c_bus[MAX_SCU_I2C]; +static int gpio_num_entry; +static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; +int sfi_mrtc_num; +int sfi_mtimer_num; + +struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX]; +EXPORT_SYMBOL_GPL(sfi_mrtc_array); + +struct blocking_notifier_head intel_scu_notifier = +			BLOCKING_NOTIFIER_INIT(intel_scu_notifier); +EXPORT_SYMBOL_GPL(intel_scu_notifier); + +#define intel_mid_sfi_get_pdata(dev, priv)	\ +	((dev)->get_platform_data ? (dev)->get_platform_data(priv) : NULL) + +/* parse all the mtimer info to a static mtimer array */ +int __init sfi_parse_mtmr(struct sfi_table_header *table) +{ +	struct sfi_table_simple *sb; +	struct sfi_timer_table_entry *pentry; +	struct mpc_intsrc mp_irq; +	int totallen; + +	sb = (struct sfi_table_simple *)table; +	if (!sfi_mtimer_num) { +		sfi_mtimer_num = SFI_GET_NUM_ENTRIES(sb, +					struct sfi_timer_table_entry); +		pentry = (struct sfi_timer_table_entry *) sb->pentry; +		totallen = sfi_mtimer_num * sizeof(*pentry); +		memcpy(sfi_mtimer_array, pentry, totallen); +	} + +	pr_debug("SFI MTIMER info (num = %d):\n", sfi_mtimer_num); +	pentry = sfi_mtimer_array; +	for (totallen = 0; totallen < sfi_mtimer_num; totallen++, pentry++) { +		pr_debug("timer[%d]: paddr = 0x%08x, freq = %dHz, irq = %d\n", +			totallen, (u32)pentry->phys_addr, +			pentry->freq_hz, pentry->irq); +			if (!pentry->irq) +				continue; +			mp_irq.type = MP_INTSRC; +			mp_irq.irqtype = mp_INT; +/* triggering mode edge bit 2-3, active high polarity bit 0-1 */ +			mp_irq.irqflag = 5; +			mp_irq.srcbus = MP_BUS_ISA; +			mp_irq.srcbusirq = pentry->irq;	/* IRQ */ +			mp_irq.dstapic = MP_APIC_ALL; +			mp_irq.dstirq = pentry->irq; +			mp_save_irq(&mp_irq); +	} + +	return 0; +} + +struct sfi_timer_table_entry *sfi_get_mtmr(int hint) +{ +	int i; +	if (hint < sfi_mtimer_num) { +		if (!sfi_mtimer_usage[hint]) { +			pr_debug("hint taken for timer %d irq %d\n", +				hint, sfi_mtimer_array[hint].irq); +			sfi_mtimer_usage[hint] = 1; +			return &sfi_mtimer_array[hint]; +		} +	} +	/* take the first timer available */ +	for (i = 0; i < sfi_mtimer_num;) { +		if (!sfi_mtimer_usage[i]) { +			sfi_mtimer_usage[i] = 1; +			return &sfi_mtimer_array[i]; +		} +		i++; +	} +	return NULL; +} + +void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr) +{ +	int i; +	for (i = 0; i < sfi_mtimer_num;) { +		if (mtmr->irq == sfi_mtimer_array[i].irq) { +			sfi_mtimer_usage[i] = 0; +			return; +		} +		i++; +	} +} + +/* parse all the mrtc info to a global mrtc array */ +int __init sfi_parse_mrtc(struct sfi_table_header *table) +{ +	struct sfi_table_simple *sb; +	struct sfi_rtc_table_entry *pentry; +	struct mpc_intsrc mp_irq; + +	int totallen; + +	sb = (struct sfi_table_simple *)table; +	if (!sfi_mrtc_num) { +		sfi_mrtc_num = SFI_GET_NUM_ENTRIES(sb, +						struct sfi_rtc_table_entry); +		pentry = (struct sfi_rtc_table_entry *)sb->pentry; +		totallen = sfi_mrtc_num * sizeof(*pentry); +		memcpy(sfi_mrtc_array, pentry, totallen); +	} + +	pr_debug("SFI RTC info (num = %d):\n", sfi_mrtc_num); +	pentry = sfi_mrtc_array; +	for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) { +		pr_debug("RTC[%d]: paddr = 0x%08x, irq = %d\n", +			totallen, (u32)pentry->phys_addr, pentry->irq); +		mp_irq.type = MP_INTSRC; +		mp_irq.irqtype = mp_INT; +		mp_irq.irqflag = 0xf;	/* level trigger and active low */ +		mp_irq.srcbus = MP_BUS_ISA; +		mp_irq.srcbusirq = pentry->irq;	/* IRQ */ +		mp_irq.dstapic = MP_APIC_ALL; +		mp_irq.dstirq = pentry->irq; +		mp_save_irq(&mp_irq); +	} +	return 0; +} + + +/* + * Parsing GPIO table first, since the DEVS table will need this table + * to map the pin name to the actual pin. + */ +static int __init sfi_parse_gpio(struct sfi_table_header *table) +{ +	struct sfi_table_simple *sb; +	struct sfi_gpio_table_entry *pentry; +	int num, i; + +	if (gpio_table) +		return 0; +	sb = (struct sfi_table_simple *)table; +	num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry); +	pentry = (struct sfi_gpio_table_entry *)sb->pentry; + +	gpio_table = kmalloc(num * sizeof(*pentry), GFP_KERNEL); +	if (!gpio_table) +		return -1; +	memcpy(gpio_table, pentry, num * sizeof(*pentry)); +	gpio_num_entry = num; + +	pr_debug("GPIO pin info:\n"); +	for (i = 0; i < num; i++, pentry++) +		pr_debug("info[%2d]: controller = %16.16s, pin_name = %16.16s," +		" pin = %d\n", i, +			pentry->controller_name, +			pentry->pin_name, +			pentry->pin_no); +	return 0; +} + +int get_gpio_by_name(const char *name) +{ +	struct sfi_gpio_table_entry *pentry = gpio_table; +	int i; + +	if (!pentry) +		return -1; +	for (i = 0; i < gpio_num_entry; i++, pentry++) { +		if (!strncmp(name, pentry->pin_name, SFI_NAME_LEN)) +			return pentry->pin_no; +	} +	return -EINVAL; +} + +void __init intel_scu_device_register(struct platform_device *pdev) +{ +	if (ipc_next_dev == MAX_IPCDEVS) +		pr_err("too many SCU IPC devices"); +	else +		ipc_devs[ipc_next_dev++] = pdev; +} + +static void __init intel_scu_spi_device_register(struct spi_board_info *sdev) +{ +	struct spi_board_info *new_dev; + +	if (spi_next_dev == MAX_SCU_SPI) { +		pr_err("too many SCU SPI devices"); +		return; +	} + +	new_dev = kzalloc(sizeof(*sdev), GFP_KERNEL); +	if (!new_dev) { +		pr_err("failed to alloc mem for delayed spi dev %s\n", +			sdev->modalias); +		return; +	} +	*new_dev = *sdev; + +	spi_devs[spi_next_dev++] = new_dev; +} + +static void __init intel_scu_i2c_device_register(int bus, +						struct i2c_board_info *idev) +{ +	struct i2c_board_info *new_dev; + +	if (i2c_next_dev == MAX_SCU_I2C) { +		pr_err("too many SCU I2C devices"); +		return; +	} + +	new_dev = kzalloc(sizeof(*idev), GFP_KERNEL); +	if (!new_dev) { +		pr_err("failed to alloc mem for delayed i2c dev %s\n", +			idev->type); +		return; +	} +	*new_dev = *idev; + +	i2c_bus[i2c_next_dev] = bus; +	i2c_devs[i2c_next_dev++] = new_dev; +} + +/* Called by IPC driver */ +void intel_scu_devices_create(void) +{ +	int i; + +	for (i = 0; i < ipc_next_dev; i++) +		platform_device_add(ipc_devs[i]); + +	for (i = 0; i < spi_next_dev; i++) +		spi_register_board_info(spi_devs[i], 1); + +	for (i = 0; i < i2c_next_dev; i++) { +		struct i2c_adapter *adapter; +		struct i2c_client *client; + +		adapter = i2c_get_adapter(i2c_bus[i]); +		if (adapter) { +			client = i2c_new_device(adapter, i2c_devs[i]); +			if (!client) +				pr_err("can't create i2c device %s\n", +					i2c_devs[i]->type); +		} else +			i2c_register_board_info(i2c_bus[i], i2c_devs[i], 1); +	} +	intel_scu_notifier_post(SCU_AVAILABLE, NULL); +} +EXPORT_SYMBOL_GPL(intel_scu_devices_create); + +/* Called by IPC driver */ +void intel_scu_devices_destroy(void) +{ +	int i; + +	intel_scu_notifier_post(SCU_DOWN, NULL); + +	for (i = 0; i < ipc_next_dev; i++) +		platform_device_del(ipc_devs[i]); +} +EXPORT_SYMBOL_GPL(intel_scu_devices_destroy); + +static void __init install_irq_resource(struct platform_device *pdev, int irq) +{ +	/* Single threaded */ +	static struct resource res __initdata = { +		.name = "IRQ", +		.flags = IORESOURCE_IRQ, +	}; +	res.start = irq; +	platform_device_add_resources(pdev, &res, 1); +} + +static void __init sfi_handle_ipc_dev(struct sfi_device_table_entry *pentry, +					struct devs_id *dev) +{ +	struct platform_device *pdev; +	void *pdata = NULL; + +	pr_debug("IPC bus, name = %16.16s, irq = 0x%2x\n", +		pentry->name, pentry->irq); +	pdata = intel_mid_sfi_get_pdata(dev, pentry); +	if (IS_ERR(pdata)) +		return; + +	pdev = platform_device_alloc(pentry->name, 0); +	if (pdev == NULL) { +		pr_err("out of memory for SFI platform device '%s'.\n", +			pentry->name); +		return; +	} +	install_irq_resource(pdev, pentry->irq); + +	pdev->dev.platform_data = pdata; +	platform_device_add(pdev); +} + +static void __init sfi_handle_spi_dev(struct sfi_device_table_entry *pentry, +					struct devs_id *dev) +{ +	struct spi_board_info spi_info; +	void *pdata = NULL; + +	memset(&spi_info, 0, sizeof(spi_info)); +	strncpy(spi_info.modalias, pentry->name, SFI_NAME_LEN); +	spi_info.irq = ((pentry->irq == (u8)0xff) ? 0 : pentry->irq); +	spi_info.bus_num = pentry->host_num; +	spi_info.chip_select = pentry->addr; +	spi_info.max_speed_hz = pentry->max_freq; +	pr_debug("SPI bus=%d, name=%16.16s, irq=0x%2x, max_freq=%d, cs=%d\n", +		spi_info.bus_num, +		spi_info.modalias, +		spi_info.irq, +		spi_info.max_speed_hz, +		spi_info.chip_select); + +	pdata = intel_mid_sfi_get_pdata(dev, &spi_info); +	if (IS_ERR(pdata)) +		return; + +	spi_info.platform_data = pdata; +	if (dev->delay) +		intel_scu_spi_device_register(&spi_info); +	else +		spi_register_board_info(&spi_info, 1); +} + +static void __init sfi_handle_i2c_dev(struct sfi_device_table_entry *pentry, +					struct devs_id *dev) +{ +	struct i2c_board_info i2c_info; +	void *pdata = NULL; + +	memset(&i2c_info, 0, sizeof(i2c_info)); +	strncpy(i2c_info.type, pentry->name, SFI_NAME_LEN); +	i2c_info.irq = ((pentry->irq == (u8)0xff) ? 0 : pentry->irq); +	i2c_info.addr = pentry->addr; +	pr_debug("I2C bus = %d, name = %16.16s, irq = 0x%2x, addr = 0x%x\n", +		pentry->host_num, +		i2c_info.type, +		i2c_info.irq, +		i2c_info.addr); +	pdata = intel_mid_sfi_get_pdata(dev, &i2c_info); +	i2c_info.platform_data = pdata; +	if (IS_ERR(pdata)) +		return; + +	if (dev->delay) +		intel_scu_i2c_device_register(pentry->host_num, &i2c_info); +	else +		i2c_register_board_info(pentry->host_num, &i2c_info, 1); +} + +extern struct devs_id *const __x86_intel_mid_dev_start[], +		      *const __x86_intel_mid_dev_end[]; + +static struct devs_id __init *get_device_id(u8 type, char *name) +{ +	struct devs_id *const *dev_table; + +	for (dev_table = __x86_intel_mid_dev_start; +			dev_table < __x86_intel_mid_dev_end; dev_table++) { +		struct devs_id *dev = *dev_table; +		if (dev->type == type && +			!strncmp(dev->name, name, SFI_NAME_LEN)) { +			return dev; +		} +	} + +	return NULL; +} + +static int __init sfi_parse_devs(struct sfi_table_header *table) +{ +	struct sfi_table_simple *sb; +	struct sfi_device_table_entry *pentry; +	struct devs_id *dev = NULL; +	int num, i; +	int ioapic; +	struct io_apic_irq_attr irq_attr; + +	sb = (struct sfi_table_simple *)table; +	num = SFI_GET_NUM_ENTRIES(sb, struct sfi_device_table_entry); +	pentry = (struct sfi_device_table_entry *)sb->pentry; + +	for (i = 0; i < num; i++, pentry++) { +		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(irq); +			if (ioapic >= 0) { +				irq_attr.ioapic = ioapic; +				irq_attr.ioapic_pin = irq; +				irq_attr.trigger = 1; +				if (intel_mid_identify_cpu() == +						INTEL_MID_CPU_CHIP_TANGIER) { +					if (!strncmp(pentry->name, +							"r69001-ts-i2c", 13)) +						/* active low */ +						irq_attr.polarity = 1; +					else if (!strncmp(pentry->name, +							"synaptics_3202", 14)) +						/* active low */ +						irq_attr.polarity = 1; +					else if (irq == 41) +						/* fast_int_1 */ +						irq_attr.polarity = 1; +					else +						/* active high */ +						irq_attr.polarity = 0; +				} else { +					/* PNW and CLV go with active low */ +					irq_attr.polarity = 1; +				} +				io_apic_set_pci_routing(NULL, irq, &irq_attr); +			} +		} else { +			irq = 0; /* No irq */ +		} + +		dev = get_device_id(pentry->type, pentry->name); + +		if (!dev) +			continue; + +		if (dev->device_handler) { +			dev->device_handler(pentry, dev); +		} else { +			switch (pentry->type) { +			case SFI_DEV_TYPE_IPC: +				sfi_handle_ipc_dev(pentry, dev); +				break; +			case SFI_DEV_TYPE_SPI: +				sfi_handle_spi_dev(pentry, dev); +				break; +			case SFI_DEV_TYPE_I2C: +				sfi_handle_i2c_dev(pentry, dev); +				break; +			case SFI_DEV_TYPE_UART: +			case SFI_DEV_TYPE_HSI: +			default: +				break; +			} +		} +	} +	return 0; +} + +static int __init intel_mid_platform_init(void) +{ +	sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, sfi_parse_gpio); +	sfi_table_parse(SFI_SIG_DEVS, NULL, NULL, sfi_parse_devs); +	return 0; +} +arch_initcall(intel_mid_platform_init); diff --git a/arch/x86/platform/iris/iris.c b/arch/x86/platform/iris/iris.c index e6cb80f620a..4d171e8640e 100644 --- a/arch/x86/platform/iris/iris.c +++ b/arch/x86/platform/iris/iris.c @@ -27,7 +27,6 @@  #include <linux/kernel.h>  #include <linux/errno.h>  #include <linux/delay.h> -#include <linux/init.h>  #include <linux/pm.h>  #include <asm/io.h> diff --git a/arch/x86/platform/mrst/Makefile b/arch/x86/platform/mrst/Makefile deleted file mode 100644 index af1da7e623f..00000000000 --- a/arch/x86/platform/mrst/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -obj-$(CONFIG_X86_INTEL_MID)	+= mrst.o -obj-$(CONFIG_X86_INTEL_MID)	+= vrtc.o -obj-$(CONFIG_EARLY_PRINTK_INTEL_MID)	+= early_printk_mrst.o diff --git a/arch/x86/platform/mrst/mrst.c b/arch/x86/platform/mrst/mrst.c deleted file mode 100644 index 3ca5957b7a3..00000000000 --- a/arch/x86/platform/mrst/mrst.c +++ /dev/null @@ -1,1052 +0,0 @@ -/* - * mrst.c: Intel Moorestown platform specific setup code - * - * (C) Copyright 2008 Intel Corporation - * Author: Jacob Pan (jacob.jun.pan@intel.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; version 2 - * of the License. - */ - -#define pr_fmt(fmt) "mrst: " fmt - -#include <linux/init.h> -#include <linux/kernel.h> -#include <linux/interrupt.h> -#include <linux/scatterlist.h> -#include <linux/sfi.h> -#include <linux/intel_pmic_gpio.h> -#include <linux/spi/spi.h> -#include <linux/i2c.h> -#include <linux/platform_data/pca953x.h> -#include <linux/gpio_keys.h> -#include <linux/input.h> -#include <linux/platform_device.h> -#include <linux/irq.h> -#include <linux/module.h> -#include <linux/notifier.h> -#include <linux/mfd/intel_msic.h> -#include <linux/gpio.h> -#include <linux/i2c/tc35876x.h> - -#include <asm/setup.h> -#include <asm/mpspec_def.h> -#include <asm/hw_irq.h> -#include <asm/apic.h> -#include <asm/io_apic.h> -#include <asm/mrst.h> -#include <asm/mrst-vrtc.h> -#include <asm/io.h> -#include <asm/i8259.h> -#include <asm/intel_scu_ipc.h> -#include <asm/apb_timer.h> -#include <asm/reboot.h> - -/* - * the clockevent devices on Moorestown/Medfield can be APBT or LAPIC clock, - * cmdline option x86_mrst_timer can be used to override the configuration - * to prefer one or the other. - * at runtime, there are basically three timer configurations: - * 1. per cpu apbt clock only - * 2. per cpu always-on lapic clocks only, this is Penwell/Medfield only - * 3. per cpu lapic clock (C3STOP) and one apbt clock, with broadcast. - * - * by default (without cmdline option), platform code first detects cpu type - * to see if we are on lincroft or penwell, then set up both lapic or apbt - * clocks accordingly. - * i.e. by default, medfield uses configuration #2, moorestown uses #1. - * config #3 is supported but not recommended on medfield. - * - * rating and feature summary: - * lapic (with C3STOP) --------- 100 - * apbt (always-on) ------------ 110 - * lapic (always-on,ARAT) ------ 150 - */ - -enum mrst_timer_options mrst_timer_options; - -static u32 sfi_mtimer_usage[SFI_MTMR_MAX_NUM]; -static struct sfi_timer_table_entry sfi_mtimer_array[SFI_MTMR_MAX_NUM]; -enum mrst_cpu_type __mrst_cpu_chip; -EXPORT_SYMBOL_GPL(__mrst_cpu_chip); - -int sfi_mtimer_num; - -struct sfi_rtc_table_entry sfi_mrtc_array[SFI_MRTC_MAX]; -EXPORT_SYMBOL_GPL(sfi_mrtc_array); -int sfi_mrtc_num; - -static void mrst_power_off(void) -{ -} - -static void mrst_reboot(void) -{ -	intel_scu_ipc_simple_command(IPCMSG_COLD_BOOT, 0); -} - -/* parse all the mtimer info to a static mtimer array */ -static int __init sfi_parse_mtmr(struct sfi_table_header *table) -{ -	struct sfi_table_simple *sb; -	struct sfi_timer_table_entry *pentry; -	struct mpc_intsrc mp_irq; -	int totallen; - -	sb = (struct sfi_table_simple *)table; -	if (!sfi_mtimer_num) { -		sfi_mtimer_num = SFI_GET_NUM_ENTRIES(sb, -					struct sfi_timer_table_entry); -		pentry = (struct sfi_timer_table_entry *) sb->pentry; -		totallen = sfi_mtimer_num * sizeof(*pentry); -		memcpy(sfi_mtimer_array, pentry, totallen); -	} - -	pr_debug("SFI MTIMER info (num = %d):\n", sfi_mtimer_num); -	pentry = sfi_mtimer_array; -	for (totallen = 0; totallen < sfi_mtimer_num; totallen++, pentry++) { -		pr_debug("timer[%d]: paddr = 0x%08x, freq = %dHz," -			" irq = %d\n", totallen, (u32)pentry->phys_addr, -			pentry->freq_hz, pentry->irq); -			if (!pentry->irq) -				continue; -			mp_irq.type = MP_INTSRC; -			mp_irq.irqtype = mp_INT; -/* triggering mode edge bit 2-3, active high polarity bit 0-1 */ -			mp_irq.irqflag = 5; -			mp_irq.srcbus = MP_BUS_ISA; -			mp_irq.srcbusirq = pentry->irq;	/* IRQ */ -			mp_irq.dstapic = MP_APIC_ALL; -			mp_irq.dstirq = pentry->irq; -			mp_save_irq(&mp_irq); -	} - -	return 0; -} - -struct sfi_timer_table_entry *sfi_get_mtmr(int hint) -{ -	int i; -	if (hint < sfi_mtimer_num) { -		if (!sfi_mtimer_usage[hint]) { -			pr_debug("hint taken for timer %d irq %d\n",\ -				hint, sfi_mtimer_array[hint].irq); -			sfi_mtimer_usage[hint] = 1; -			return &sfi_mtimer_array[hint]; -		} -	} -	/* take the first timer available */ -	for (i = 0; i < sfi_mtimer_num;) { -		if (!sfi_mtimer_usage[i]) { -			sfi_mtimer_usage[i] = 1; -			return &sfi_mtimer_array[i]; -		} -		i++; -	} -	return NULL; -} - -void sfi_free_mtmr(struct sfi_timer_table_entry *mtmr) -{ -	int i; -	for (i = 0; i < sfi_mtimer_num;) { -		if (mtmr->irq == sfi_mtimer_array[i].irq) { -			sfi_mtimer_usage[i] = 0; -			return; -		} -		i++; -	} -} - -/* parse all the mrtc info to a global mrtc array */ -int __init sfi_parse_mrtc(struct sfi_table_header *table) -{ -	struct sfi_table_simple *sb; -	struct sfi_rtc_table_entry *pentry; -	struct mpc_intsrc mp_irq; - -	int totallen; - -	sb = (struct sfi_table_simple *)table; -	if (!sfi_mrtc_num) { -		sfi_mrtc_num = SFI_GET_NUM_ENTRIES(sb, -						struct sfi_rtc_table_entry); -		pentry = (struct sfi_rtc_table_entry *)sb->pentry; -		totallen = sfi_mrtc_num * sizeof(*pentry); -		memcpy(sfi_mrtc_array, pentry, totallen); -	} - -	pr_debug("SFI RTC info (num = %d):\n", sfi_mrtc_num); -	pentry = sfi_mrtc_array; -	for (totallen = 0; totallen < sfi_mrtc_num; totallen++, pentry++) { -		pr_debug("RTC[%d]: paddr = 0x%08x, irq = %d\n", -			totallen, (u32)pentry->phys_addr, pentry->irq); -		mp_irq.type = MP_INTSRC; -		mp_irq.irqtype = mp_INT; -		mp_irq.irqflag = 0xf;	/* level trigger and active low */ -		mp_irq.srcbus = MP_BUS_ISA; -		mp_irq.srcbusirq = pentry->irq;	/* IRQ */ -		mp_irq.dstapic = MP_APIC_ALL; -		mp_irq.dstirq = pentry->irq; -		mp_save_irq(&mp_irq); -	} -	return 0; -} - -static unsigned long __init mrst_calibrate_tsc(void) -{ -	unsigned long fast_calibrate; -	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); -	 -	if (fast_calibrate) -		return fast_calibrate; - -	return 0; -} - -static void __init mrst_time_init(void) -{ -	sfi_table_parse(SFI_SIG_MTMR, NULL, NULL, sfi_parse_mtmr); -	switch (mrst_timer_options) { -	case MRST_TIMER_APBT_ONLY: -		break; -	case MRST_TIMER_LAPIC_APBT: -		x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; -		x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; -		break; -	default: -		if (!boot_cpu_has(X86_FEATURE_ARAT)) -			break; -		x86_init.timers.setup_percpu_clockev = setup_boot_APIC_clock; -		x86_cpuinit.setup_percpu_clockev = setup_secondary_APIC_clock; -		return; -	} -	/* we need at least one APB timer */ -	pre_init_apic_IRQ0(); -	apbt_time_init(); -} - -static void mrst_arch_setup(void) -{ -	if (boot_cpu_data.x86 == 6 && boot_cpu_data.x86_model == 0x27) -		__mrst_cpu_chip = MRST_CPU_CHIP_PENWELL; -	else { -		pr_err("Unknown Intel MID CPU (%d:%d), default to Penwell\n", -			boot_cpu_data.x86, boot_cpu_data.x86_model); -		__mrst_cpu_chip = MRST_CPU_CHIP_PENWELL; -	} -} - -/* MID systems don't have i8042 controller */ -static int mrst_i8042_detect(void) -{ -	return 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. - */ -void __init x86_mrst_early_setup(void) -{ -	x86_init.resources.probe_roms = x86_init_noop; -	x86_init.resources.reserve_resources = x86_init_noop; - -	x86_init.timers.timer_init = mrst_time_init; -	x86_init.timers.setup_percpu_clockev = x86_init_noop; - -	x86_init.irqs.pre_vector_init = x86_init_noop; - -	x86_init.oem.arch_setup = mrst_arch_setup; - -	x86_cpuinit.setup_percpu_clockev = apbt_setup_secondary_clock; - -	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; - -	legacy_pic = &null_legacy_pic; - -	/* Moorestown specific power_off/restart method */ -	pm_power_off = mrst_power_off; -	machine_ops.emergency_restart  = mrst_reboot; - -	/* Avoid searching for BIOS MP tables */ -	x86_init.mpparse.find_smp_config = x86_init_noop; -	x86_init.mpparse.get_smp_config = x86_init_uint_noop; -	set_bit(MP_BUS_ISA, mp_bus_not_pci); -} - -/* - * if user does not want to use per CPU apb timer, just give it a lower rating - * than local apic timer and skip the late per cpu timer init. - */ -static inline int __init setup_x86_mrst_timer(char *arg) -{ -	if (!arg) -		return -EINVAL; - -	if (strcmp("apbt_only", arg) == 0) -		mrst_timer_options = MRST_TIMER_APBT_ONLY; -	else if (strcmp("lapic_and_apbt", arg) == 0) -		mrst_timer_options = MRST_TIMER_LAPIC_APBT; -	else { -		pr_warning("X86 MRST timer option %s not recognised" -			   " use x86_mrst_timer=apbt_only or lapic_and_apbt\n", -			   arg); -		return -EINVAL; -	} -	return 0; -} -__setup("x86_mrst_timer=", setup_x86_mrst_timer); - -/* - * Parsing GPIO table first, since the DEVS table will need this table - * to map the pin name to the actual pin. - */ -static struct sfi_gpio_table_entry *gpio_table; -static int gpio_num_entry; - -static int __init sfi_parse_gpio(struct sfi_table_header *table) -{ -	struct sfi_table_simple *sb; -	struct sfi_gpio_table_entry *pentry; -	int num, i; - -	if (gpio_table) -		return 0; -	sb = (struct sfi_table_simple *)table; -	num = SFI_GET_NUM_ENTRIES(sb, struct sfi_gpio_table_entry); -	pentry = (struct sfi_gpio_table_entry *)sb->pentry; - -	gpio_table = kmalloc(num * sizeof(*pentry), GFP_KERNEL); -	if (!gpio_table) -		return -1; -	memcpy(gpio_table, pentry, num * sizeof(*pentry)); -	gpio_num_entry = num; - -	pr_debug("GPIO pin info:\n"); -	for (i = 0; i < num; i++, pentry++) -		pr_debug("info[%2d]: controller = %16.16s, pin_name = %16.16s," -		" pin = %d\n", i, -			pentry->controller_name, -			pentry->pin_name, -			pentry->pin_no); -	return 0; -} - -static int get_gpio_by_name(const char *name) -{ -	struct sfi_gpio_table_entry *pentry = gpio_table; -	int i; - -	if (!pentry) -		return -1; -	for (i = 0; i < gpio_num_entry; i++, pentry++) { -		if (!strncmp(name, pentry->pin_name, SFI_NAME_LEN)) -			return pentry->pin_no; -	} -	return -1; -} - -/* - * Here defines the array of devices platform data that IAFW would export - * through SFI "DEVS" table, we use name and type to match the device and - * its platform data. - */ -struct devs_id { -	char name[SFI_NAME_LEN + 1]; -	u8 type; -	u8 delay; -	void *(*get_platform_data)(void *info); -}; - -/* the offset for the mapping of global gpio pin to irq */ -#define MRST_IRQ_OFFSET 0x100 - -static void __init *pmic_gpio_platform_data(void *info) -{ -	static struct intel_pmic_gpio_platform_data pmic_gpio_pdata; -	int gpio_base = get_gpio_by_name("pmic_gpio_base"); - -	if (gpio_base == -1) -		gpio_base = 64; -	pmic_gpio_pdata.gpio_base = gpio_base; -	pmic_gpio_pdata.irq_base = gpio_base + MRST_IRQ_OFFSET; -	pmic_gpio_pdata.gpiointr = 0xffffeff8; - -	return &pmic_gpio_pdata; -} - -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; -	return NULL; -} - -/* we have multiple max7315 on the board ... */ -#define MAX7315_NUM 2 -static void __init *max7315_platform_data(void *info) -{ -	static struct pca953x_platform_data max7315_pdata[MAX7315_NUM]; -	static int nr; -	struct pca953x_platform_data *max7315 = &max7315_pdata[nr]; -	struct i2c_board_info *i2c_info = info; -	int gpio_base, intr; -	char base_pin_name[SFI_NAME_LEN + 1]; -	char intr_pin_name[SFI_NAME_LEN + 1]; - -	if (nr == MAX7315_NUM) { -		pr_err("too many max7315s, we only support %d\n", -				MAX7315_NUM); -		return NULL; -	} -	/* we have several max7315 on the board, we only need load several -	 * instances of the same pca953x driver to cover them -	 */ -	strcpy(i2c_info->type, "max7315"); -	if (nr++) { -		sprintf(base_pin_name, "max7315_%d_base", nr); -		sprintf(intr_pin_name, "max7315_%d_int", nr); -	} else { -		strcpy(base_pin_name, "max7315_base"); -		strcpy(intr_pin_name, "max7315_int"); -	} - -	gpio_base = get_gpio_by_name(base_pin_name); -	intr = get_gpio_by_name(intr_pin_name); - -	if (gpio_base == -1) -		return NULL; -	max7315->gpio_base = gpio_base; -	if (intr != -1) { -		i2c_info->irq = intr + MRST_IRQ_OFFSET; -		max7315->irq_base = gpio_base + MRST_IRQ_OFFSET; -	} else { -		i2c_info->irq = -1; -		max7315->irq_base = -1; -	} -	return max7315; -} - -static void *tca6416_platform_data(void *info) -{ -	static struct pca953x_platform_data tca6416; -	struct i2c_board_info *i2c_info = info; -	int gpio_base, intr; -	char base_pin_name[SFI_NAME_LEN + 1]; -	char intr_pin_name[SFI_NAME_LEN + 1]; - -	strcpy(i2c_info->type, "tca6416"); -	strcpy(base_pin_name, "tca6416_base"); -	strcpy(intr_pin_name, "tca6416_int"); - -	gpio_base = get_gpio_by_name(base_pin_name); -	intr = get_gpio_by_name(intr_pin_name); - -	if (gpio_base == -1) -		return NULL; -	tca6416.gpio_base = gpio_base; -	if (intr != -1) { -		i2c_info->irq = intr + MRST_IRQ_OFFSET; -		tca6416.irq_base = gpio_base + MRST_IRQ_OFFSET; -	} else { -		i2c_info->irq = -1; -		tca6416.irq_base = -1; -	} -	return &tca6416; -} - -static void *mpu3050_platform_data(void *info) -{ -	struct i2c_board_info *i2c_info = info; -	int intr = get_gpio_by_name("mpu3050_int"); - -	if (intr == -1) -		return NULL; - -	i2c_info->irq = intr + MRST_IRQ_OFFSET; -	return NULL; -} - -static void __init *emc1403_platform_data(void *info) -{ -	static short intr2nd_pdata; -	struct i2c_board_info *i2c_info = info; -	int intr = get_gpio_by_name("thermal_int"); -	int intr2nd = get_gpio_by_name("thermal_alert"); - -	if (intr == -1 || intr2nd == -1) -		return NULL; - -	i2c_info->irq = intr + MRST_IRQ_OFFSET; -	intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; - -	return &intr2nd_pdata; -} - -static void __init *lis331dl_platform_data(void *info) -{ -	static short intr2nd_pdata; -	struct i2c_board_info *i2c_info = info; -	int intr = get_gpio_by_name("accel_int"); -	int intr2nd = get_gpio_by_name("accel_2"); - -	if (intr == -1 || intr2nd == -1) -		return NULL; - -	i2c_info->irq = intr + MRST_IRQ_OFFSET; -	intr2nd_pdata = intr2nd + MRST_IRQ_OFFSET; - -	return &intr2nd_pdata; -} - -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 void *msic_thermal_platform_data(void *info) -{ -	return msic_generic_platform_data(info, INTEL_MSIC_BLOCK_THERMAL); -} - -/* tc35876x DSI-LVDS bridge chip and panel platform data */ -static void *tc35876x_platform_data(void *data) -{ -       static struct tc35876x_platform_data pdata; - -       /* gpio pins set to -1 will not be used by the driver */ -       pdata.gpio_bridge_reset = get_gpio_by_name("LCMB_RXEN"); -       pdata.gpio_panel_bl_en = get_gpio_by_name("6S6P_BL_EN"); -       pdata.gpio_panel_vadd = get_gpio_by_name("EN_VREG_LCD_V3P3"); - -       return &pdata; -} - -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}, -	{"pmic_gpio", SFI_DEV_TYPE_IPC, 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}, -	{"i2c_max7315_2", SFI_DEV_TYPE_I2C, 1, &max7315_platform_data}, -	{"tca6416", SFI_DEV_TYPE_I2C, 1, &tca6416_platform_data}, -	{"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}, -	{"mpu3050", SFI_DEV_TYPE_I2C, 1, &mpu3050_platform_data}, -	{"i2c_disp_brig", SFI_DEV_TYPE_I2C, 0, &tc35876x_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}, -	{"msic_thermal", SFI_DEV_TYPE_IPC, 1, &msic_thermal_platform_data}, - -	{}, -}; - -#define MAX_IPCDEVS	24 -static struct platform_device *ipc_devs[MAX_IPCDEVS]; -static int ipc_next_dev; - -#define MAX_SCU_SPI	24 -static struct spi_board_info *spi_devs[MAX_SCU_SPI]; -static int spi_next_dev; - -#define MAX_SCU_I2C	24 -static struct i2c_board_info *i2c_devs[MAX_SCU_I2C]; -static int i2c_bus[MAX_SCU_I2C]; -static int i2c_next_dev; - -static void __init intel_scu_device_register(struct platform_device *pdev) -{ -	if(ipc_next_dev == MAX_IPCDEVS) -		pr_err("too many SCU IPC devices"); -	else -		ipc_devs[ipc_next_dev++] = pdev; -} - -static void __init intel_scu_spi_device_register(struct spi_board_info *sdev) -{ -	struct spi_board_info *new_dev; - -	if (spi_next_dev == MAX_SCU_SPI) { -		pr_err("too many SCU SPI devices"); -		return; -	} - -	new_dev = kzalloc(sizeof(*sdev), GFP_KERNEL); -	if (!new_dev) { -		pr_err("failed to alloc mem for delayed spi dev %s\n", -			sdev->modalias); -		return; -	} -	memcpy(new_dev, sdev, sizeof(*sdev)); - -	spi_devs[spi_next_dev++] = new_dev; -} - -static void __init intel_scu_i2c_device_register(int bus, -						struct i2c_board_info *idev) -{ -	struct i2c_board_info *new_dev; - -	if (i2c_next_dev == MAX_SCU_I2C) { -		pr_err("too many SCU I2C devices"); -		return; -	} - -	new_dev = kzalloc(sizeof(*idev), GFP_KERNEL); -	if (!new_dev) { -		pr_err("failed to alloc mem for delayed i2c dev %s\n", -			idev->type); -		return; -	} -	memcpy(new_dev, idev, sizeof(*idev)); - -	i2c_bus[i2c_next_dev] = 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) -{ -	int i; - -	for (i = 0; i < ipc_next_dev; i++) -		platform_device_add(ipc_devs[i]); - -	for (i = 0; i < spi_next_dev; i++) -		spi_register_board_info(spi_devs[i], 1); - -	for (i = 0; i < i2c_next_dev; i++) { -		struct i2c_adapter *adapter; -		struct i2c_client *client; - -		adapter = i2c_get_adapter(i2c_bus[i]); -		if (adapter) { -			client = i2c_new_device(adapter, i2c_devs[i]); -			if (!client) -				pr_err("can't create i2c device %s\n", -					i2c_devs[i]->type); -		} else -			i2c_register_board_info(i2c_bus[i], i2c_devs[i], 1); -	} -	intel_scu_notifier_post(SCU_AVAILABLE, NULL); -} -EXPORT_SYMBOL_GPL(intel_scu_devices_create); - -/* Called by IPC driver */ -void intel_scu_devices_destroy(void) -{ -	int i; - -	intel_scu_notifier_post(SCU_DOWN, NULL); - -	for (i = 0; i < ipc_next_dev; i++) -		platform_device_del(ipc_devs[i]); -} -EXPORT_SYMBOL_GPL(intel_scu_devices_destroy); - -static void __init install_irq_resource(struct platform_device *pdev, int irq) -{ -	/* Single threaded */ -	static struct resource __initdata res = { -		.name = "IRQ", -		.flags = IORESOURCE_IRQ, -	}; -	res.start = irq; -	platform_device_add_resources(pdev, &res, 1); -} - -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, 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; - -	pdev = platform_device_alloc(entry->name, 0); -	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); -} - -static void __init sfi_handle_spi_dev(struct spi_board_info *spi_info) -{ -	const struct devs_id *dev = device_ids; -	void *pdata = NULL; - -	while (dev->name[0]) { -		if (dev->type == SFI_DEV_TYPE_SPI && -				!strncmp(dev->name, spi_info->modalias, SFI_NAME_LEN)) { -			pdata = dev->get_platform_data(spi_info); -			break; -		} -		dev++; -	} -	spi_info->platform_data = pdata; -	if (dev->delay) -		intel_scu_spi_device_register(spi_info); -	else -		spi_register_board_info(spi_info, 1); -} - -static void __init sfi_handle_i2c_dev(int bus, struct i2c_board_info *i2c_info) -{ -	const struct devs_id *dev = device_ids; -	void *pdata = NULL; - -	while (dev->name[0]) { -		if (dev->type == SFI_DEV_TYPE_I2C && -			!strncmp(dev->name, i2c_info->type, SFI_NAME_LEN)) { -			pdata = dev->get_platform_data(i2c_info); -			break; -		} -		dev++; -	} -	i2c_info->platform_data = pdata; - -	if (dev->delay) -		intel_scu_i2c_device_register(bus, i2c_info); -	else -		i2c_register_board_info(bus, i2c_info, 1); - } - - -static int __init sfi_parse_devs(struct sfi_table_header *table) -{ -	struct sfi_table_simple *sb; -	struct sfi_device_table_entry *pentry; -	struct spi_board_info spi_info; -	struct i2c_board_info i2c_info; -	int num, i, bus; -	int ioapic; -	struct io_apic_irq_attr irq_attr; - -	sb = (struct sfi_table_simple *)table; -	num = SFI_GET_NUM_ENTRIES(sb, struct sfi_device_table_entry); -	pentry = (struct sfi_device_table_entry *)sb->pentry; - -	for (i = 0; i < num; i++, pentry++) { -		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(irq); -			irq_attr.ioapic = ioapic; -			irq_attr.ioapic_pin = irq; -			irq_attr.trigger = 1; -			irq_attr.polarity = 1; -			io_apic_set_pci_routing(NULL, irq, &irq_attr); -		} else -			irq = 0; /* No irq */ - -		switch (pentry->type) { -		case SFI_DEV_TYPE_IPC: -			pr_debug("info[%2d]: IPC bus, name = %16.16s, " -				"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)); -			strncpy(spi_info.modalias, pentry->name, SFI_NAME_LEN); -			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; -			pr_debug("info[%2d]: SPI bus = %d, name = %16.16s, " -				"irq = 0x%2x, max_freq = %d, cs = %d\n", i, -				spi_info.bus_num, -				spi_info.modalias, -				spi_info.irq, -				spi_info.max_speed_hz, -				spi_info.chip_select); -			sfi_handle_spi_dev(&spi_info); -			break; -		case SFI_DEV_TYPE_I2C: -			memset(&i2c_info, 0, sizeof(i2c_info)); -			bus = pentry->host_num; -			strncpy(i2c_info.type, pentry->name, SFI_NAME_LEN); -			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, -				i2c_info.type, -				i2c_info.irq, -				i2c_info.addr); -			sfi_handle_i2c_dev(bus, &i2c_info); -			break; -		case SFI_DEV_TYPE_UART: -		case SFI_DEV_TYPE_HSI: -		default: -			; -		} -	} -	return 0; -} - -static int __init mrst_platform_init(void) -{ -	sfi_table_parse(SFI_SIG_GPIO, NULL, NULL, sfi_parse_gpio); -	sfi_table_parse(SFI_SIG_DEVS, NULL, NULL, sfi_parse_devs); -	return 0; -} -arch_initcall(mrst_platform_init); - -/* - * we will search these buttons in SFI GPIO table (by name) - * and register them dynamically. Please add all possible - * buttons here, we will shrink them if no GPIO found. - */ -static struct gpio_keys_button gpio_button[] = { -	{KEY_POWER,		-1, 1, "power_btn",	EV_KEY, 0, 3000}, -	{KEY_PROG1,		-1, 1, "prog_btn1",	EV_KEY, 0, 20}, -	{KEY_PROG2,		-1, 1, "prog_btn2",	EV_KEY, 0, 20}, -	{SW_LID,		-1, 1, "lid_switch",	EV_SW,  0, 20}, -	{KEY_VOLUMEUP,		-1, 1, "vol_up",	EV_KEY, 0, 20}, -	{KEY_VOLUMEDOWN,	-1, 1, "vol_down",	EV_KEY, 0, 20}, -	{KEY_CAMERA,		-1, 1, "camera_full",	EV_KEY, 0, 20}, -	{KEY_CAMERA_FOCUS,	-1, 1, "camera_half",	EV_KEY, 0, 20}, -	{SW_KEYPAD_SLIDE,	-1, 1, "MagSw1",	EV_SW,  0, 20}, -	{SW_KEYPAD_SLIDE,	-1, 1, "MagSw2",	EV_SW,  0, 20}, -}; - -static struct gpio_keys_platform_data mrst_gpio_keys = { -	.buttons	= gpio_button, -	.rep		= 1, -	.nbuttons	= -1, /* will fill it after search */ -}; - -static struct platform_device pb_device = { -	.name		= "gpio-keys", -	.id		= -1, -	.dev		= { -		.platform_data	= &mrst_gpio_keys, -	}, -}; - -/* - * Shrink the non-existent buttons, register the gpio button - * device if there is some - */ -static int __init pb_keys_init(void) -{ -	struct gpio_keys_button *gb = gpio_button; -	int i, num, good = 0; - -	num = sizeof(gpio_button) / sizeof(struct gpio_keys_button); -	for (i = 0; i < num; i++) { -		gb[i].gpio = get_gpio_by_name(gb[i].desc); -		pr_debug("info[%2d]: name = %s, gpio = %d\n", i, gb[i].desc, gb[i].gpio); -		if (gb[i].gpio == -1) -			continue; - -		if (i != good) -			gb[good] = gb[i]; -		good++; -	} - -	if (good) { -		mrst_gpio_keys.nbuttons = good; -		return platform_device_register(&pb_device); -	} -	return 0; -} -late_initcall(pb_keys_init); diff --git a/arch/x86/platform/olpc/olpc-xo1-pm.c b/arch/x86/platform/olpc/olpc-xo1-pm.c index ff0174dda81..a9acde72d4e 100644 --- a/arch/x86/platform/olpc/olpc-xo1-pm.c +++ b/arch/x86/platform/olpc/olpc-xo1-pm.c @@ -75,7 +75,7 @@ static int xo1_power_state_enter(suspend_state_t pm_state)  	return 0;  } -asmlinkage int xo1_do_sleep(u8 sleep_state) +asmlinkage __visible int xo1_do_sleep(u8 sleep_state)  {  	void *pgd_addr = __va(read_cr3()); diff --git a/arch/x86/platform/olpc/olpc-xo15-sci.c b/arch/x86/platform/olpc/olpc-xo15-sci.c index fef7d0ba7e3..08e350e757d 100644 --- a/arch/x86/platform/olpc/olpc-xo15-sci.c +++ b/arch/x86/platform/olpc/olpc-xo15-sci.c @@ -15,8 +15,7 @@  #include <linux/power_supply.h>  #include <linux/olpc-ec.h> -#include <acpi/acpi_bus.h> -#include <acpi/acpi_drivers.h> +#include <linux/acpi.h>  #include <asm/olpc.h>  #define DRV_NAME			"olpc-xo15-sci" @@ -40,16 +39,9 @@ static bool				lid_wake_on_close;   */  static int set_lid_wake_behavior(bool wake_on_close)  { -	struct acpi_object_list arg_list; -	union acpi_object arg;  	acpi_status status; -	arg_list.count		= 1; -	arg_list.pointer	= &arg; -	arg.type		= ACPI_TYPE_INTEGER; -	arg.integer.value	= wake_on_close; - -	status = acpi_evaluate_object(NULL, "\\_SB.PCI0.LID.LIDW", &arg_list, NULL); +	status = acpi_execute_simple_method(NULL, "\\_SB.PCI0.LID.LIDW", wake_on_close);  	if (ACPI_FAILURE(status)) {  		pr_warning(PFX "failed to set lid behavior\n");  		return 1; diff --git a/arch/x86/platform/ts5500/ts5500.c b/arch/x86/platform/ts5500/ts5500.c index 39febb214e8..9471b9456f2 100644 --- a/arch/x86/platform/ts5500/ts5500.c +++ b/arch/x86/platform/ts5500/ts5500.c @@ -88,7 +88,7 @@ struct ts5500_sbc {  static const struct {  	const char * const string;  	const ssize_t offset; -} ts5500_signatures[] __initdata = { +} ts5500_signatures[] __initconst = {  	{ "TS-5x00 AMD Elan", 0xb14 },  }; diff --git a/arch/x86/platform/uv/Makefile b/arch/x86/platform/uv/Makefile index 6c40995fefb..52079bebd01 100644 --- a/arch/x86/platform/uv/Makefile +++ b/arch/x86/platform/uv/Makefile @@ -1 +1 @@ -obj-$(CONFIG_X86_UV)		+= tlb_uv.o bios_uv.o uv_irq.o uv_sysfs.o uv_time.o +obj-$(CONFIG_X86_UV)		+= tlb_uv.o bios_uv.o uv_irq.o uv_sysfs.o uv_time.o uv_nmi.o diff --git a/arch/x86/platform/uv/bios_uv.c b/arch/x86/platform/uv/bios_uv.c index 766612137a6..1584cbed0dc 100644 --- a/arch/x86/platform/uv/bios_uv.c +++ b/arch/x86/platform/uv/bios_uv.c @@ -39,7 +39,7 @@ s64 uv_bios_call(enum uv_bios_cmd which, u64 a1, u64 a2, u64 a3, u64 a4, u64 a5)  		 */  		return BIOS_STATUS_UNIMPLEMENTED; -	ret = efi_call6((void *)__va(tab->function), (u64)which, +	ret = efi_call((void *)__va(tab->function), (u64)which,  			a1, a2, a3, a4, a5);  	return ret;  } diff --git a/arch/x86/platform/uv/tlb_uv.c b/arch/x86/platform/uv/tlb_uv.c index 0f92173a12b..dfe605ac1bc 100644 --- a/arch/x86/platform/uv/tlb_uv.c +++ b/arch/x86/platform/uv/tlb_uv.c @@ -433,15 +433,49 @@ static void reset_with_ipi(struct pnmask *distribution, struct bau_control *bcp)  	return;  } -static inline unsigned long cycles_2_us(unsigned long long cyc) +/* + * Not to be confused with cycles_2_ns() from tsc.c; this gives a relative + * number, not an absolute. It converts a duration in cycles to a duration in + * ns. + */ +static inline unsigned long long cycles_2_ns(unsigned long long cyc)  { +	struct cyc2ns_data *data = cyc2ns_read_begin();  	unsigned long long ns; -	unsigned long us; -	int cpu = smp_processor_id(); -	ns =  (cyc * per_cpu(cyc2ns, cpu)) >> CYC2NS_SCALE_FACTOR; -	us = ns / 1000; -	return us; +	ns = mul_u64_u32_shr(cyc, data->cyc2ns_mul, data->cyc2ns_shift); + +	cyc2ns_read_end(data); +	return ns; +} + +/* + * The reverse of the above; converts a duration in ns to a duration in cycles. + */  +static inline unsigned long long ns_2_cycles(unsigned long long ns) +{ +	struct cyc2ns_data *data = cyc2ns_read_begin(); +	unsigned long long cyc; + +	cyc = (ns << data->cyc2ns_shift) / data->cyc2ns_mul; + +	cyc2ns_read_end(data); +	return cyc; +} + +static inline unsigned long cycles_2_us(unsigned long long cyc) +{ +	return cycles_2_ns(cyc) / NSEC_PER_USEC; +} + +static inline cycles_t sec_2_cycles(unsigned long sec) +{ +	return ns_2_cycles(sec * NSEC_PER_SEC); +} + +static inline unsigned long long usec_2_cycles(unsigned long usec) +{ +	return ns_2_cycles(usec * NSEC_PER_USEC);  }  /* @@ -668,16 +702,6 @@ static int wait_completion(struct bau_desc *bau_desc,  								bcp, try);  } -static inline cycles_t sec_2_cycles(unsigned long sec) -{ -	unsigned long ns; -	cycles_t cyc; - -	ns = sec * 1000000000; -	cyc = (ns << CYC2NS_SCALE_FACTOR)/(per_cpu(cyc2ns, smp_processor_id())); -	return cyc; -} -  /*   * Our retries are blocked by all destination sw ack resources being   * in use, and a timeout is pending. In that case hardware immediately @@ -1070,12 +1094,13 @@ const struct cpumask *uv_flush_tlb_others(const struct cpumask *cpumask,  	unsigned long status;  	bcp = &per_cpu(bau_control, cpu); -	stat = bcp->statp; -	stat->s_enters++;  	if (bcp->nobau)  		return cpumask; +	stat = bcp->statp; +	stat->s_enters++; +  	if (bcp->busy) {  		descriptor_status =  			read_lmmr(UVH_LB_BAU_SB_ACTIVATION_STATUS_0); @@ -1326,16 +1351,6 @@ static void ptc_seq_stop(struct seq_file *file, void *data)  {  } -static inline unsigned long long usec_2_cycles(unsigned long microsec) -{ -	unsigned long ns; -	unsigned long long cyc; - -	ns = microsec * 1000; -	cyc = (ns << CYC2NS_SCALE_FACTOR)/(per_cpu(cyc2ns, smp_processor_id())); -	return cyc; -} -  /*   * Display the statistics thru /proc/sgi_uv/ptc_statistics   * 'data' points to the cpu number diff --git a/arch/x86/platform/uv/uv_irq.c b/arch/x86/platform/uv/uv_irq.c index acf7752da95..b233681af4d 100644 --- a/arch/x86/platform/uv/uv_irq.c +++ b/arch/x86/platform/uv/uv_irq.c @@ -238,11 +238,9 @@ uv_set_irq_affinity(struct irq_data *data, const struct cpumask *mask,  int uv_setup_irq(char *irq_name, int cpu, int mmr_blade,  		 unsigned long mmr_offset, int limit)  { -	int irq, ret; +	int ret, irq = irq_alloc_hwirq(uv_blade_to_memory_nid(mmr_blade)); -	irq = create_irq_nr(NR_IRQS_LEGACY, uv_blade_to_memory_nid(mmr_blade)); - -	if (irq <= 0) +	if (!irq)  		return -EBUSY;  	ret = arch_enable_uv_irq(irq_name, irq, cpu, mmr_blade, mmr_offset, @@ -250,7 +248,7 @@ int uv_setup_irq(char *irq_name, int cpu, int mmr_blade,  	if (ret == irq)  		uv_set_irq_2_mmr_info(irq, mmr_offset, mmr_blade);  	else -		destroy_irq(irq); +		irq_free_hwirq(irq);  	return ret;  } @@ -285,6 +283,6 @@ void uv_teardown_irq(unsigned int irq)  			n = n->rb_right;  	}  	spin_unlock_irqrestore(&uv_irq_lock, irqflags); -	destroy_irq(irq); +	irq_free_hwirq(irq);  }  EXPORT_SYMBOL_GPL(uv_teardown_irq); diff --git a/arch/x86/platform/uv/uv_nmi.c b/arch/x86/platform/uv/uv_nmi.c new file mode 100644 index 00000000000..c89c93320c1 --- /dev/null +++ b/arch/x86/platform/uv/uv_nmi.c @@ -0,0 +1,727 @@ +/* + * SGI NMI support routines + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License + *  along with this program; if not, write to the Free Software + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA + * + *  Copyright (c) 2009-2013 Silicon Graphics, Inc.  All Rights Reserved. + *  Copyright (c) Mike Travis + */ + +#include <linux/cpu.h> +#include <linux/delay.h> +#include <linux/kdb.h> +#include <linux/kexec.h> +#include <linux/kgdb.h> +#include <linux/module.h> +#include <linux/nmi.h> +#include <linux/sched.h> +#include <linux/slab.h> + +#include <asm/apic.h> +#include <asm/current.h> +#include <asm/kdebug.h> +#include <asm/local64.h> +#include <asm/nmi.h> +#include <asm/traps.h> +#include <asm/uv/uv.h> +#include <asm/uv/uv_hub.h> +#include <asm/uv/uv_mmrs.h> + +/* + * UV handler for NMI + * + * Handle system-wide NMI events generated by the global 'power nmi' command. + * + * Basic operation is to field the NMI interrupt on each cpu and wait + * until all cpus have arrived into the nmi handler.  If some cpus do not + * make it into the handler, try and force them in with the IPI(NMI) signal. + * + * We also have to lessen UV Hub MMR accesses as much as possible as this + * disrupts the UV Hub's primary mission of directing NumaLink traffic and + * can cause system problems to occur. + * + * To do this we register our primary NMI notifier on the NMI_UNKNOWN + * chain.  This reduces the number of false NMI calls when the perf + * tools are running which generate an enormous number of NMIs per + * second (~4M/s for 1024 cpu threads).  Our secondary NMI handler is + * very short as it only checks that if it has been "pinged" with the + * IPI(NMI) signal as mentioned above, and does not read the UV Hub's MMR. + * + */ + +static struct uv_hub_nmi_s **uv_hub_nmi_list; + +DEFINE_PER_CPU(struct uv_cpu_nmi_s, __uv_cpu_nmi); +EXPORT_PER_CPU_SYMBOL_GPL(__uv_cpu_nmi); + +static unsigned long nmi_mmr; +static unsigned long nmi_mmr_clear; +static unsigned long nmi_mmr_pending; + +static atomic_t	uv_in_nmi; +static atomic_t uv_nmi_cpu = ATOMIC_INIT(-1); +static atomic_t uv_nmi_cpus_in_nmi = ATOMIC_INIT(-1); +static atomic_t uv_nmi_slave_continue; +static cpumask_var_t uv_nmi_cpu_mask; + +/* Values for uv_nmi_slave_continue */ +#define SLAVE_CLEAR	0 +#define SLAVE_CONTINUE	1 +#define SLAVE_EXIT	2 + +/* + * Default is all stack dumps go to the console and buffer. + * Lower level to send to log buffer only. + */ +static int uv_nmi_loglevel = CONSOLE_LOGLEVEL_DEFAULT; +module_param_named(dump_loglevel, uv_nmi_loglevel, int, 0644); + +/* + * The following values show statistics on how perf events are affecting + * this system. + */ +static int param_get_local64(char *buffer, const struct kernel_param *kp) +{ +	return sprintf(buffer, "%lu\n", local64_read((local64_t *)kp->arg)); +} + +static int param_set_local64(const char *val, const struct kernel_param *kp) +{ +	/* clear on any write */ +	local64_set((local64_t *)kp->arg, 0); +	return 0; +} + +static struct kernel_param_ops param_ops_local64 = { +	.get = param_get_local64, +	.set = param_set_local64, +}; +#define param_check_local64(name, p) __param_check(name, p, local64_t) + +static local64_t uv_nmi_count; +module_param_named(nmi_count, uv_nmi_count, local64, 0644); + +static local64_t uv_nmi_misses; +module_param_named(nmi_misses, uv_nmi_misses, local64, 0644); + +static local64_t uv_nmi_ping_count; +module_param_named(ping_count, uv_nmi_ping_count, local64, 0644); + +static local64_t uv_nmi_ping_misses; +module_param_named(ping_misses, uv_nmi_ping_misses, local64, 0644); + +/* + * Following values allow tuning for large systems under heavy loading + */ +static int uv_nmi_initial_delay = 100; +module_param_named(initial_delay, uv_nmi_initial_delay, int, 0644); + +static int uv_nmi_slave_delay = 100; +module_param_named(slave_delay, uv_nmi_slave_delay, int, 0644); + +static int uv_nmi_loop_delay = 100; +module_param_named(loop_delay, uv_nmi_loop_delay, int, 0644); + +static int uv_nmi_trigger_delay = 10000; +module_param_named(trigger_delay, uv_nmi_trigger_delay, int, 0644); + +static int uv_nmi_wait_count = 100; +module_param_named(wait_count, uv_nmi_wait_count, int, 0644); + +static int uv_nmi_retry_count = 500; +module_param_named(retry_count, uv_nmi_retry_count, int, 0644); + +/* + * Valid NMI Actions: + *  "dump"	- dump process stack for each cpu + *  "ips"	- dump IP info for each cpu + *  "kdump"	- do crash dump + *  "kdb"	- enter KDB (default) + *  "kgdb"	- enter KGDB + */ +static char uv_nmi_action[8] = "kdb"; +module_param_string(action, uv_nmi_action, sizeof(uv_nmi_action), 0644); + +static inline bool uv_nmi_action_is(const char *action) +{ +	return (strncmp(uv_nmi_action, action, strlen(action)) == 0); +} + +/* Setup which NMI support is present in system */ +static void uv_nmi_setup_mmrs(void) +{ +	if (uv_read_local_mmr(UVH_NMI_MMRX_SUPPORTED)) { +		uv_write_local_mmr(UVH_NMI_MMRX_REQ, +					1UL << UVH_NMI_MMRX_REQ_SHIFT); +		nmi_mmr = UVH_NMI_MMRX; +		nmi_mmr_clear = UVH_NMI_MMRX_CLEAR; +		nmi_mmr_pending = 1UL << UVH_NMI_MMRX_SHIFT; +		pr_info("UV: SMI NMI support: %s\n", UVH_NMI_MMRX_TYPE); +	} else { +		nmi_mmr = UVH_NMI_MMR; +		nmi_mmr_clear = UVH_NMI_MMR_CLEAR; +		nmi_mmr_pending = 1UL << UVH_NMI_MMR_SHIFT; +		pr_info("UV: SMI NMI support: %s\n", UVH_NMI_MMR_TYPE); +	} +} + +/* Read NMI MMR and check if NMI flag was set by BMC. */ +static inline int uv_nmi_test_mmr(struct uv_hub_nmi_s *hub_nmi) +{ +	hub_nmi->nmi_value = uv_read_local_mmr(nmi_mmr); +	atomic_inc(&hub_nmi->read_mmr_count); +	return !!(hub_nmi->nmi_value & nmi_mmr_pending); +} + +static inline void uv_local_mmr_clear_nmi(void) +{ +	uv_write_local_mmr(nmi_mmr_clear, nmi_mmr_pending); +} + +/* + * If first cpu in on this hub, set hub_nmi "in_nmi" and "owner" values and + * return true.  If first cpu in on the system, set global "in_nmi" flag. + */ +static int uv_set_in_nmi(int cpu, struct uv_hub_nmi_s *hub_nmi) +{ +	int first = atomic_add_unless(&hub_nmi->in_nmi, 1, 1); + +	if (first) { +		atomic_set(&hub_nmi->cpu_owner, cpu); +		if (atomic_add_unless(&uv_in_nmi, 1, 1)) +			atomic_set(&uv_nmi_cpu, cpu); + +		atomic_inc(&hub_nmi->nmi_count); +	} +	return first; +} + +/* Check if this is a system NMI event */ +static int uv_check_nmi(struct uv_hub_nmi_s *hub_nmi) +{ +	int cpu = smp_processor_id(); +	int nmi = 0; + +	local64_inc(&uv_nmi_count); +	uv_cpu_nmi.queries++; + +	do { +		nmi = atomic_read(&hub_nmi->in_nmi); +		if (nmi) +			break; + +		if (raw_spin_trylock(&hub_nmi->nmi_lock)) { + +			/* check hub MMR NMI flag */ +			if (uv_nmi_test_mmr(hub_nmi)) { +				uv_set_in_nmi(cpu, hub_nmi); +				nmi = 1; +				break; +			} + +			/* MMR NMI flag is clear */ +			raw_spin_unlock(&hub_nmi->nmi_lock); + +		} else { +			/* wait a moment for the hub nmi locker to set flag */ +			cpu_relax(); +			udelay(uv_nmi_slave_delay); + +			/* re-check hub in_nmi flag */ +			nmi = atomic_read(&hub_nmi->in_nmi); +			if (nmi) +				break; +		} + +		/* check if this BMC missed setting the MMR NMI flag */ +		if (!nmi) { +			nmi = atomic_read(&uv_in_nmi); +			if (nmi) +				uv_set_in_nmi(cpu, hub_nmi); +		} + +	} while (0); + +	if (!nmi) +		local64_inc(&uv_nmi_misses); + +	return nmi; +} + +/* Need to reset the NMI MMR register, but only once per hub. */ +static inline void uv_clear_nmi(int cpu) +{ +	struct uv_hub_nmi_s *hub_nmi = uv_hub_nmi; + +	if (cpu == atomic_read(&hub_nmi->cpu_owner)) { +		atomic_set(&hub_nmi->cpu_owner, -1); +		atomic_set(&hub_nmi->in_nmi, 0); +		uv_local_mmr_clear_nmi(); +		raw_spin_unlock(&hub_nmi->nmi_lock); +	} +} + +/* Print non-responding cpus */ +static void uv_nmi_nr_cpus_pr(char *fmt) +{ +	static char cpu_list[1024]; +	int len = sizeof(cpu_list); +	int c = cpumask_weight(uv_nmi_cpu_mask); +	int n = cpulist_scnprintf(cpu_list, len, uv_nmi_cpu_mask); + +	if (n >= len-1) +		strcpy(&cpu_list[len - 6], "...\n"); + +	printk(fmt, c, cpu_list); +} + +/* Ping non-responding cpus attemping to force them into the NMI handler */ +static void uv_nmi_nr_cpus_ping(void) +{ +	int cpu; + +	for_each_cpu(cpu, uv_nmi_cpu_mask) +		atomic_set(&uv_cpu_nmi_per(cpu).pinging, 1); + +	apic->send_IPI_mask(uv_nmi_cpu_mask, APIC_DM_NMI); +} + +/* Clean up flags for cpus that ignored both NMI and ping */ +static void uv_nmi_cleanup_mask(void) +{ +	int cpu; + +	for_each_cpu(cpu, uv_nmi_cpu_mask) { +		atomic_set(&uv_cpu_nmi_per(cpu).pinging, 0); +		atomic_set(&uv_cpu_nmi_per(cpu).state, UV_NMI_STATE_OUT); +		cpumask_clear_cpu(cpu, uv_nmi_cpu_mask); +	} +} + +/* Loop waiting as cpus enter nmi handler */ +static int uv_nmi_wait_cpus(int first) +{ +	int i, j, k, n = num_online_cpus(); +	int last_k = 0, waiting = 0; + +	if (first) { +		cpumask_copy(uv_nmi_cpu_mask, cpu_online_mask); +		k = 0; +	} else { +		k = n - cpumask_weight(uv_nmi_cpu_mask); +	} + +	udelay(uv_nmi_initial_delay); +	for (i = 0; i < uv_nmi_retry_count; i++) { +		int loop_delay = uv_nmi_loop_delay; + +		for_each_cpu(j, uv_nmi_cpu_mask) { +			if (atomic_read(&uv_cpu_nmi_per(j).state)) { +				cpumask_clear_cpu(j, uv_nmi_cpu_mask); +				if (++k >= n) +					break; +			} +		} +		if (k >= n) {		/* all in? */ +			k = n; +			break; +		} +		if (last_k != k) {	/* abort if no new cpus coming in */ +			last_k = k; +			waiting = 0; +		} else if (++waiting > uv_nmi_wait_count) +			break; + +		/* extend delay if waiting only for cpu 0 */ +		if (waiting && (n - k) == 1 && +		    cpumask_test_cpu(0, uv_nmi_cpu_mask)) +			loop_delay *= 100; + +		udelay(loop_delay); +	} +	atomic_set(&uv_nmi_cpus_in_nmi, k); +	return n - k; +} + +/* Wait until all slave cpus have entered UV NMI handler */ +static void uv_nmi_wait(int master) +{ +	/* indicate this cpu is in */ +	atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_IN); + +	/* if not the first cpu in (the master), then we are a slave cpu */ +	if (!master) +		return; + +	do { +		/* wait for all other cpus to gather here */ +		if (!uv_nmi_wait_cpus(1)) +			break; + +		/* if not all made it in, send IPI NMI to them */ +		uv_nmi_nr_cpus_pr(KERN_ALERT +			"UV: Sending NMI IPI to %d non-responding CPUs: %s\n"); +		uv_nmi_nr_cpus_ping(); + +		/* if all cpus are in, then done */ +		if (!uv_nmi_wait_cpus(0)) +			break; + +		uv_nmi_nr_cpus_pr(KERN_ALERT +			"UV: %d CPUs not in NMI loop: %s\n"); +	} while (0); + +	pr_alert("UV: %d of %d CPUs in NMI\n", +		atomic_read(&uv_nmi_cpus_in_nmi), num_online_cpus()); +} + +static void uv_nmi_dump_cpu_ip_hdr(void) +{ +	printk(KERN_DEFAULT +		"\nUV: %4s %6s %-32s %s   (Note: PID 0 not listed)\n", +		"CPU", "PID", "COMMAND", "IP"); +} + +static void uv_nmi_dump_cpu_ip(int cpu, struct pt_regs *regs) +{ +	printk(KERN_DEFAULT "UV: %4d %6d %-32.32s ", +		cpu, current->pid, current->comm); + +	printk_address(regs->ip); +} + +/* Dump this cpu's state */ +static void uv_nmi_dump_state_cpu(int cpu, struct pt_regs *regs) +{ +	const char *dots = " ................................. "; + +	if (uv_nmi_action_is("ips")) { +		if (cpu == 0) +			uv_nmi_dump_cpu_ip_hdr(); + +		if (current->pid != 0) +			uv_nmi_dump_cpu_ip(cpu, regs); + +	} else if (uv_nmi_action_is("dump")) { +		printk(KERN_DEFAULT +			"UV:%sNMI process trace for CPU %d\n", dots, cpu); +		show_regs(regs); +	} +	atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_DUMP_DONE); +} + +/* Trigger a slave cpu to dump it's state */ +static void uv_nmi_trigger_dump(int cpu) +{ +	int retry = uv_nmi_trigger_delay; + +	if (atomic_read(&uv_cpu_nmi_per(cpu).state) != UV_NMI_STATE_IN) +		return; + +	atomic_set(&uv_cpu_nmi_per(cpu).state, UV_NMI_STATE_DUMP); +	do { +		cpu_relax(); +		udelay(10); +		if (atomic_read(&uv_cpu_nmi_per(cpu).state) +				!= UV_NMI_STATE_DUMP) +			return; +	} while (--retry > 0); + +	pr_crit("UV: CPU %d stuck in process dump function\n", cpu); +	atomic_set(&uv_cpu_nmi_per(cpu).state, UV_NMI_STATE_DUMP_DONE); +} + +/* Wait until all cpus ready to exit */ +static void uv_nmi_sync_exit(int master) +{ +	atomic_dec(&uv_nmi_cpus_in_nmi); +	if (master) { +		while (atomic_read(&uv_nmi_cpus_in_nmi) > 0) +			cpu_relax(); +		atomic_set(&uv_nmi_slave_continue, SLAVE_CLEAR); +	} else { +		while (atomic_read(&uv_nmi_slave_continue)) +			cpu_relax(); +	} +} + +/* Walk through cpu list and dump state of each */ +static void uv_nmi_dump_state(int cpu, struct pt_regs *regs, int master) +{ +	if (master) { +		int tcpu; +		int ignored = 0; +		int saved_console_loglevel = console_loglevel; + +		pr_alert("UV: tracing %s for %d CPUs from CPU %d\n", +			uv_nmi_action_is("ips") ? "IPs" : "processes", +			atomic_read(&uv_nmi_cpus_in_nmi), cpu); + +		console_loglevel = uv_nmi_loglevel; +		atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT); +		for_each_online_cpu(tcpu) { +			if (cpumask_test_cpu(tcpu, uv_nmi_cpu_mask)) +				ignored++; +			else if (tcpu == cpu) +				uv_nmi_dump_state_cpu(tcpu, regs); +			else +				uv_nmi_trigger_dump(tcpu); +		} +		if (ignored) +			printk(KERN_DEFAULT "UV: %d CPUs ignored NMI\n", +				ignored); + +		console_loglevel = saved_console_loglevel; +		pr_alert("UV: process trace complete\n"); +	} else { +		while (!atomic_read(&uv_nmi_slave_continue)) +			cpu_relax(); +		while (atomic_read(&uv_cpu_nmi.state) != UV_NMI_STATE_DUMP) +			cpu_relax(); +		uv_nmi_dump_state_cpu(cpu, regs); +	} +	uv_nmi_sync_exit(master); +} + +static void uv_nmi_touch_watchdogs(void) +{ +	touch_softlockup_watchdog_sync(); +	clocksource_touch_watchdog(); +	rcu_cpu_stall_reset(); +	touch_nmi_watchdog(); +} + +#if defined(CONFIG_KEXEC) +static atomic_t uv_nmi_kexec_failed; +static void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs) +{ +	/* Call crash to dump system state */ +	if (master) { +		pr_emerg("UV: NMI executing crash_kexec on CPU%d\n", cpu); +		crash_kexec(regs); + +		pr_emerg("UV: crash_kexec unexpectedly returned, "); +		if (!kexec_crash_image) { +			pr_cont("crash kernel not loaded\n"); +			atomic_set(&uv_nmi_kexec_failed, 1); +			uv_nmi_sync_exit(1); +			return; +		} +		pr_cont("kexec busy, stalling cpus while waiting\n"); +	} + +	/* If crash exec fails the slaves should return, otherwise stall */ +	while (atomic_read(&uv_nmi_kexec_failed) == 0) +		mdelay(10); + +	/* Crash kernel most likely not loaded, return in an orderly fashion */ +	uv_nmi_sync_exit(0); +} + +#else /* !CONFIG_KEXEC */ +static inline void uv_nmi_kdump(int cpu, int master, struct pt_regs *regs) +{ +	if (master) +		pr_err("UV: NMI kdump: KEXEC not supported in this kernel\n"); +} +#endif /* !CONFIG_KEXEC */ + +#ifdef CONFIG_KGDB +#ifdef CONFIG_KGDB_KDB +static inline int uv_nmi_kdb_reason(void) +{ +	return KDB_REASON_SYSTEM_NMI; +} +#else /* !CONFIG_KGDB_KDB */ +static inline int uv_nmi_kdb_reason(void) +{ +	/* Insure user is expecting to attach gdb remote */ +	if (uv_nmi_action_is("kgdb")) +		return 0; + +	pr_err("UV: NMI error: KDB is not enabled in this kernel\n"); +	return -1; +} +#endif /* CONFIG_KGDB_KDB */ + +/* + * Call KGDB/KDB from NMI handler + * + * Note that if both KGDB and KDB are configured, then the action of 'kgdb' or + * 'kdb' has no affect on which is used.  See the KGDB documention for further + * information. + */ +static void uv_call_kgdb_kdb(int cpu, struct pt_regs *regs, int master) +{ +	if (master) { +		int reason = uv_nmi_kdb_reason(); +		int ret; + +		if (reason < 0) +			return; + +		/* call KGDB NMI handler as MASTER */ +		ret = kgdb_nmicallin(cpu, X86_TRAP_NMI, regs, reason, +				&uv_nmi_slave_continue); +		if (ret) { +			pr_alert("KGDB returned error, is kgdboc set?\n"); +			atomic_set(&uv_nmi_slave_continue, SLAVE_EXIT); +		} +	} else { +		/* wait for KGDB signal that it's ready for slaves to enter */ +		int sig; + +		do { +			cpu_relax(); +			sig = atomic_read(&uv_nmi_slave_continue); +		} while (!sig); + +		/* call KGDB as slave */ +		if (sig == SLAVE_CONTINUE) +			kgdb_nmicallback(cpu, regs); +	} +	uv_nmi_sync_exit(master); +} + +#else /* !CONFIG_KGDB */ +static inline void uv_call_kgdb_kdb(int cpu, struct pt_regs *regs, int master) +{ +	pr_err("UV: NMI error: KGDB is not enabled in this kernel\n"); +} +#endif /* !CONFIG_KGDB */ + +/* + * UV NMI handler + */ +int uv_handle_nmi(unsigned int reason, struct pt_regs *regs) +{ +	struct uv_hub_nmi_s *hub_nmi = uv_hub_nmi; +	int cpu = smp_processor_id(); +	int master = 0; +	unsigned long flags; + +	local_irq_save(flags); + +	/* If not a UV System NMI, ignore */ +	if (!atomic_read(&uv_cpu_nmi.pinging) && !uv_check_nmi(hub_nmi)) { +		local_irq_restore(flags); +		return NMI_DONE; +	} + +	/* Indicate we are the first CPU into the NMI handler */ +	master = (atomic_read(&uv_nmi_cpu) == cpu); + +	/* If NMI action is "kdump", then attempt to do it */ +	if (uv_nmi_action_is("kdump")) +		uv_nmi_kdump(cpu, master, regs); + +	/* Pause as all cpus enter the NMI handler */ +	uv_nmi_wait(master); + +	/* Dump state of each cpu */ +	if (uv_nmi_action_is("ips") || uv_nmi_action_is("dump")) +		uv_nmi_dump_state(cpu, regs, master); + +	/* Call KGDB/KDB if enabled */ +	else if (uv_nmi_action_is("kdb") || uv_nmi_action_is("kgdb")) +		uv_call_kgdb_kdb(cpu, regs, master); + +	/* Clear per_cpu "in nmi" flag */ +	atomic_set(&uv_cpu_nmi.state, UV_NMI_STATE_OUT); + +	/* Clear MMR NMI flag on each hub */ +	uv_clear_nmi(cpu); + +	/* Clear global flags */ +	if (master) { +		if (cpumask_weight(uv_nmi_cpu_mask)) +			uv_nmi_cleanup_mask(); +		atomic_set(&uv_nmi_cpus_in_nmi, -1); +		atomic_set(&uv_nmi_cpu, -1); +		atomic_set(&uv_in_nmi, 0); +	} + +	uv_nmi_touch_watchdogs(); +	local_irq_restore(flags); + +	return NMI_HANDLED; +} + +/* + * NMI handler for pulling in CPUs when perf events are grabbing our NMI + */ +static int uv_handle_nmi_ping(unsigned int reason, struct pt_regs *regs) +{ +	int ret; + +	uv_cpu_nmi.queries++; +	if (!atomic_read(&uv_cpu_nmi.pinging)) { +		local64_inc(&uv_nmi_ping_misses); +		return NMI_DONE; +	} + +	uv_cpu_nmi.pings++; +	local64_inc(&uv_nmi_ping_count); +	ret = uv_handle_nmi(reason, regs); +	atomic_set(&uv_cpu_nmi.pinging, 0); +	return ret; +} + +static void uv_register_nmi_notifier(void) +{ +	if (register_nmi_handler(NMI_UNKNOWN, uv_handle_nmi, 0, "uv")) +		pr_warn("UV: NMI handler failed to register\n"); + +	if (register_nmi_handler(NMI_LOCAL, uv_handle_nmi_ping, 0, "uvping")) +		pr_warn("UV: PING NMI handler failed to register\n"); +} + +void uv_nmi_init(void) +{ +	unsigned int value; + +	/* +	 * Unmask NMI on all cpus +	 */ +	value = apic_read(APIC_LVT1) | APIC_DM_NMI; +	value &= ~APIC_LVT_MASKED; +	apic_write(APIC_LVT1, value); +} + +void uv_nmi_setup(void) +{ +	int size = sizeof(void *) * (1 << NODES_SHIFT); +	int cpu, nid; + +	/* Setup hub nmi info */ +	uv_nmi_setup_mmrs(); +	uv_hub_nmi_list = kzalloc(size, GFP_KERNEL); +	pr_info("UV: NMI hub list @ 0x%p (%d)\n", uv_hub_nmi_list, size); +	BUG_ON(!uv_hub_nmi_list); +	size = sizeof(struct uv_hub_nmi_s); +	for_each_present_cpu(cpu) { +		nid = cpu_to_node(cpu); +		if (uv_hub_nmi_list[nid] == NULL) { +			uv_hub_nmi_list[nid] = kzalloc_node(size, +							    GFP_KERNEL, nid); +			BUG_ON(!uv_hub_nmi_list[nid]); +			raw_spin_lock_init(&(uv_hub_nmi_list[nid]->nmi_lock)); +			atomic_set(&uv_hub_nmi_list[nid]->cpu_owner, -1); +		} +		uv_hub_nmi_per(cpu) = uv_hub_nmi_list[nid]; +	} +	BUG_ON(!alloc_cpumask_var(&uv_nmi_cpu_mask, GFP_KERNEL)); +	uv_register_nmi_notifier(); +} diff --git a/arch/x86/platform/visws/Makefile b/arch/x86/platform/visws/Makefile deleted file mode 100644 index 91bc17ab2fd..00000000000 --- a/arch/x86/platform/visws/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_X86_VISWS)	+= visws_quirks.o diff --git a/arch/x86/platform/visws/visws_quirks.c b/arch/x86/platform/visws/visws_quirks.c deleted file mode 100644 index 94d8a39332e..00000000000 --- a/arch/x86/platform/visws/visws_quirks.c +++ /dev/null @@ -1,608 +0,0 @@ -/* - *  SGI Visual Workstation support and quirks, unmaintained. - * - *  Split out from setup.c by davej@suse.de - * - *	Copyright (C) 1999 Bent Hagemark, Ingo Molnar - * - *  SGI Visual Workstation interrupt controller - * - *  The Cobalt system ASIC in the Visual Workstation contains a "Cobalt" APIC - *  which serves as the main interrupt controller in the system.  Non-legacy - *  hardware in the system uses this controller directly.  Legacy devices - *  are connected to the PIIX4 which in turn has its 8259(s) connected to - *  a of the Cobalt APIC entry. - * - *  09/02/2000 - Updated for 2.4 by jbarnes@sgi.com - * - *  25/11/2002 - Updated for 2.5 by Andrey Panin <pazke@orbita1.ru> - */ -#include <linux/interrupt.h> -#include <linux/module.h> -#include <linux/init.h> -#include <linux/smp.h> - -#include <asm/visws/cobalt.h> -#include <asm/visws/piix4.h> -#include <asm/io_apic.h> -#include <asm/fixmap.h> -#include <asm/reboot.h> -#include <asm/setup.h> -#include <asm/apic.h> -#include <asm/e820.h> -#include <asm/time.h> -#include <asm/io.h> - -#include <linux/kernel_stat.h> - -#include <asm/i8259.h> -#include <asm/irq_vectors.h> -#include <asm/visws/lithium.h> - -#include <linux/sched.h> -#include <linux/kernel.h> -#include <linux/pci.h> -#include <linux/pci_ids.h> - -extern int no_broadcast; - -char visws_board_type	= -1; -char visws_board_rev	= -1; - -static void __init visws_time_init(void) -{ -	printk(KERN_INFO "Starting Cobalt Timer system clock\n"); - -	/* Set the countdown value */ -	co_cpu_write(CO_CPU_TIMEVAL, CO_TIME_HZ/HZ); - -	/* Start the timer */ -	co_cpu_write(CO_CPU_CTRL, co_cpu_read(CO_CPU_CTRL) | CO_CTRL_TIMERUN); - -	/* Enable (unmask) the timer interrupt */ -	co_cpu_write(CO_CPU_CTRL, co_cpu_read(CO_CPU_CTRL) & ~CO_CTRL_TIMEMASK); - -	setup_default_timer_irq(); -} - -/* Replaces the default init_ISA_irqs in the generic setup */ -static void __init visws_pre_intr_init(void); - -/* Quirk for machine specific memory setup. */ - -#define MB (1024 * 1024) - -unsigned long sgivwfb_mem_phys; -unsigned long sgivwfb_mem_size; -EXPORT_SYMBOL(sgivwfb_mem_phys); -EXPORT_SYMBOL(sgivwfb_mem_size); - -long long mem_size __initdata = 0; - -static char * __init visws_memory_setup(void) -{ -	long long gfx_mem_size = 8 * MB; - -	mem_size = boot_params.alt_mem_k; - -	if (!mem_size) { -		printk(KERN_WARNING "Bootloader didn't set memory size, upgrade it !\n"); -		mem_size = 128 * MB; -	} - -	/* -	 * this hardcodes the graphics memory to 8 MB -	 * it really should be sized dynamically (or at least -	 * set as a boot param) -	 */ -	if (!sgivwfb_mem_size) { -		printk(KERN_WARNING "Defaulting to 8 MB framebuffer size\n"); -		sgivwfb_mem_size = 8 * MB; -	} - -	/* -	 * Trim to nearest MB -	 */ -	sgivwfb_mem_size &= ~((1 << 20) - 1); -	sgivwfb_mem_phys = mem_size - gfx_mem_size; - -	e820_add_region(0, LOWMEMSIZE(), E820_RAM); -	e820_add_region(HIGH_MEMORY, mem_size - sgivwfb_mem_size - HIGH_MEMORY, E820_RAM); -	e820_add_region(sgivwfb_mem_phys, sgivwfb_mem_size, E820_RESERVED); - -	return "PROM"; -} - -static void visws_machine_emergency_restart(void) -{ -	/* -	 * Visual Workstations restart after this -	 * register is poked on the PIIX4 -	 */ -	outb(PIIX4_RESET_VAL, PIIX4_RESET_PORT); -} - -static void visws_machine_power_off(void) -{ -	unsigned short pm_status; -/*	extern unsigned int pci_bus0; */ - -	while ((pm_status = inw(PMSTS_PORT)) & 0x100) -		outw(pm_status, PMSTS_PORT); - -	outw(PM_SUSPEND_ENABLE, PMCNTRL_PORT); - -	mdelay(10); - -#define PCI_CONF1_ADDRESS(bus, devfn, reg) \ -	(0x80000000 | (bus << 16) | (devfn << 8) | (reg & ~3)) - -/*	outl(PCI_CONF1_ADDRESS(pci_bus0, SPECIAL_DEV, SPECIAL_REG), 0xCF8); */ -	outl(PIIX_SPECIAL_STOP, 0xCFC); -} - -static void __init visws_get_smp_config(unsigned int early) -{ -} - -/* - * The Visual Workstation is Intel MP compliant in the hardware - * sense, but it doesn't have a BIOS(-configuration table). - * No problem for Linux. - */ - -static void __init MP_processor_info(struct mpc_cpu *m) -{ -	int ver, logical_apicid; -	physid_mask_t apic_cpus; - -	if (!(m->cpuflag & CPU_ENABLED)) -		return; - -	logical_apicid = m->apicid; -	printk(KERN_INFO "%sCPU #%d %u:%u APIC version %d\n", -	       m->cpuflag & CPU_BOOTPROCESSOR ? "Bootup " : "", -	       m->apicid, (m->cpufeature & CPU_FAMILY_MASK) >> 8, -	       (m->cpufeature & CPU_MODEL_MASK) >> 4, m->apicver); - -	if (m->cpuflag & CPU_BOOTPROCESSOR) -		boot_cpu_physical_apicid = m->apicid; - -	ver = m->apicver; -	if ((ver >= 0x14 && m->apicid >= 0xff) || m->apicid >= 0xf) { -		printk(KERN_ERR "Processor #%d INVALID. (Max ID: %d).\n", -			m->apicid, MAX_LOCAL_APIC); -		return; -	} - -	apic->apicid_to_cpu_present(m->apicid, &apic_cpus); -	physids_or(phys_cpu_present_map, phys_cpu_present_map, apic_cpus); -	/* -	 * Validate version -	 */ -	if (ver == 0x0) { -		printk(KERN_ERR "BIOS bug, APIC version is 0 for CPU#%d! " -			"fixing up to 0x10. (tell your hw vendor)\n", -			m->apicid); -		ver = 0x10; -	} -	apic_version[m->apicid] = ver; -} - -static void __init visws_find_smp_config(void) -{ -	struct mpc_cpu *mp = phys_to_virt(CO_CPU_TAB_PHYS); -	unsigned short ncpus = readw(phys_to_virt(CO_CPU_NUM_PHYS)); - -	if (ncpus > CO_CPU_MAX) { -		printk(KERN_WARNING "find_visws_smp: got cpu count of %d at %p\n", -			ncpus, mp); - -		ncpus = CO_CPU_MAX; -	} - -	if (ncpus > setup_max_cpus) -		ncpus = setup_max_cpus; - -#ifdef CONFIG_X86_LOCAL_APIC -	smp_found_config = 1; -#endif -	while (ncpus--) -		MP_processor_info(mp++); - -	mp_lapic_addr = APIC_DEFAULT_PHYS_BASE; -} - -static void visws_trap_init(void); - -void __init visws_early_detect(void) -{ -	int raw; - -	visws_board_type = (char)(inb_p(PIIX_GPI_BD_REG) & PIIX_GPI_BD_REG) -							 >> PIIX_GPI_BD_SHIFT; - -	if (visws_board_type < 0) -		return; - -	/* -	 * Override the default platform setup functions -	 */ -	x86_init.resources.memory_setup = visws_memory_setup; -	x86_init.mpparse.get_smp_config = visws_get_smp_config; -	x86_init.mpparse.find_smp_config = visws_find_smp_config; -	x86_init.irqs.pre_vector_init = visws_pre_intr_init; -	x86_init.irqs.trap_init = visws_trap_init; -	x86_init.timers.timer_init = visws_time_init; -	x86_init.pci.init = pci_visws_init; -	x86_init.pci.init_irq = x86_init_noop; - -	/* -	 * Install reboot quirks: -	 */ -	pm_power_off			= visws_machine_power_off; -	machine_ops.emergency_restart	= visws_machine_emergency_restart; - -	/* -	 * Do not use broadcast IPIs: -	 */ -	no_broadcast = 0; - -#ifdef CONFIG_X86_IO_APIC -	/* -	 * Turn off IO-APIC detection and initialization: -	 */ -	skip_ioapic_setup		= 1; -#endif - -	/* -	 * Get Board rev. -	 * First, we have to initialize the 307 part to allow us access -	 * to the GPIO registers.  Let's map them at 0x0fc0 which is right -	 * after the PIIX4 PM section. -	 */ -	outb_p(SIO_DEV_SEL, SIO_INDEX); -	outb_p(SIO_GP_DEV, SIO_DATA);	/* Talk to GPIO regs. */ - -	outb_p(SIO_DEV_MSB, SIO_INDEX); -	outb_p(SIO_GP_MSB, SIO_DATA);	/* MSB of GPIO base address */ - -	outb_p(SIO_DEV_LSB, SIO_INDEX); -	outb_p(SIO_GP_LSB, SIO_DATA);	/* LSB of GPIO base address */ - -	outb_p(SIO_DEV_ENB, SIO_INDEX); -	outb_p(1, SIO_DATA);		/* Enable GPIO registers. */ - -	/* -	 * Now, we have to map the power management section to write -	 * a bit which enables access to the GPIO registers. -	 * What lunatic came up with this shit? -	 */ -	outb_p(SIO_DEV_SEL, SIO_INDEX); -	outb_p(SIO_PM_DEV, SIO_DATA);	/* Talk to GPIO regs. */ - -	outb_p(SIO_DEV_MSB, SIO_INDEX); -	outb_p(SIO_PM_MSB, SIO_DATA);	/* MSB of PM base address */ - -	outb_p(SIO_DEV_LSB, SIO_INDEX); -	outb_p(SIO_PM_LSB, SIO_DATA);	/* LSB of PM base address */ - -	outb_p(SIO_DEV_ENB, SIO_INDEX); -	outb_p(1, SIO_DATA);		/* Enable PM registers. */ - -	/* -	 * Now, write the PM register which enables the GPIO registers. -	 */ -	outb_p(SIO_PM_FER2, SIO_PM_INDEX); -	outb_p(SIO_PM_GP_EN, SIO_PM_DATA); - -	/* -	 * Now, initialize the GPIO registers. -	 * We want them all to be inputs which is the -	 * power on default, so let's leave them alone. -	 * So, let's just read the board rev! -	 */ -	raw = inb_p(SIO_GP_DATA1); -	raw &= 0x7f;	/* 7 bits of valid board revision ID. */ - -	if (visws_board_type == VISWS_320) { -		if (raw < 0x6) { -			visws_board_rev = 4; -		} else if (raw < 0xc) { -			visws_board_rev = 5; -		} else { -			visws_board_rev = 6; -		} -	} else if (visws_board_type == VISWS_540) { -			visws_board_rev = 2; -		} else { -			visws_board_rev = raw; -		} - -	printk(KERN_INFO "Silicon Graphics Visual Workstation %s (rev %d) detected\n", -	       (visws_board_type == VISWS_320 ? "320" : -	       (visws_board_type == VISWS_540 ? "540" : -		"unknown")), visws_board_rev); -} - -#define A01234 (LI_INTA_0 | LI_INTA_1 | LI_INTA_2 | LI_INTA_3 | LI_INTA_4) -#define BCD (LI_INTB | LI_INTC | LI_INTD) -#define ALLDEVS (A01234 | BCD) - -static __init void lithium_init(void) -{ -	set_fixmap(FIX_LI_PCIA, LI_PCI_A_PHYS); -	set_fixmap(FIX_LI_PCIB, LI_PCI_B_PHYS); - -	if ((li_pcia_read16(PCI_VENDOR_ID) != PCI_VENDOR_ID_SGI) || -	    (li_pcia_read16(PCI_DEVICE_ID) != PCI_DEVICE_ID_SGI_LITHIUM)) { -		printk(KERN_EMERG "Lithium hostbridge %c not found\n", 'A'); -/*		panic("This machine is not SGI Visual Workstation 320/540"); */ -	} - -	if ((li_pcib_read16(PCI_VENDOR_ID) != PCI_VENDOR_ID_SGI) || -	    (li_pcib_read16(PCI_DEVICE_ID) != PCI_DEVICE_ID_SGI_LITHIUM)) { -		printk(KERN_EMERG "Lithium hostbridge %c not found\n", 'B'); -/*		panic("This machine is not SGI Visual Workstation 320/540"); */ -	} - -	li_pcia_write16(LI_PCI_INTEN, ALLDEVS); -	li_pcib_write16(LI_PCI_INTEN, ALLDEVS); -} - -static __init void cobalt_init(void) -{ -	/* -	 * On normal SMP PC this is used only with SMP, but we have to -	 * use it and set it up here to start the Cobalt clock -	 */ -	set_fixmap(FIX_APIC_BASE, APIC_DEFAULT_PHYS_BASE); -	setup_local_APIC(); -	printk(KERN_INFO "Local APIC Version %#x, ID %#x\n", -		(unsigned int)apic_read(APIC_LVR), -		(unsigned int)apic_read(APIC_ID)); - -	set_fixmap(FIX_CO_CPU, CO_CPU_PHYS); -	set_fixmap(FIX_CO_APIC, CO_APIC_PHYS); -	printk(KERN_INFO "Cobalt Revision %#lx, APIC ID %#lx\n", -		co_cpu_read(CO_CPU_REV), co_apic_read(CO_APIC_ID)); - -	/* Enable Cobalt APIC being careful to NOT change the ID! */ -	co_apic_write(CO_APIC_ID, co_apic_read(CO_APIC_ID) | CO_APIC_ENABLE); - -	printk(KERN_INFO "Cobalt APIC enabled: ID reg %#lx\n", -		co_apic_read(CO_APIC_ID)); -} - -static void __init visws_trap_init(void) -{ -	lithium_init(); -	cobalt_init(); -} - -/* - * IRQ controller / APIC support: - */ - -static DEFINE_SPINLOCK(cobalt_lock); - -/* - * Set the given Cobalt APIC Redirection Table entry to point - * to the given IDT vector/index. - */ -static inline void co_apic_set(int entry, int irq) -{ -	co_apic_write(CO_APIC_LO(entry), CO_APIC_LEVEL | (irq + FIRST_EXTERNAL_VECTOR)); -	co_apic_write(CO_APIC_HI(entry), 0); -} - -/* - * Cobalt (IO)-APIC functions to handle PCI devices. - */ -static inline int co_apic_ide0_hack(void) -{ -	extern char visws_board_type; -	extern char visws_board_rev; - -	if (visws_board_type == VISWS_320 && visws_board_rev == 5) -		return 5; -	return CO_APIC_IDE0; -} - -static int is_co_apic(unsigned int irq) -{ -	if (IS_CO_APIC(irq)) -		return CO_APIC(irq); - -	switch (irq) { -		case 0: return CO_APIC_CPU; -		case CO_IRQ_IDE0: return co_apic_ide0_hack(); -		case CO_IRQ_IDE1: return CO_APIC_IDE1; -		default: return -1; -	} -} - - -/* - * This is the SGI Cobalt (IO-)APIC: - */ -static void enable_cobalt_irq(struct irq_data *data) -{ -	co_apic_set(is_co_apic(data->irq), data->irq); -} - -static void disable_cobalt_irq(struct irq_data *data) -{ -	int entry = is_co_apic(data->irq); - -	co_apic_write(CO_APIC_LO(entry), CO_APIC_MASK); -	co_apic_read(CO_APIC_LO(entry)); -} - -static void ack_cobalt_irq(struct irq_data *data) -{ -	unsigned long flags; - -	spin_lock_irqsave(&cobalt_lock, flags); -	disable_cobalt_irq(data); -	apic_write(APIC_EOI, APIC_EOI_ACK); -	spin_unlock_irqrestore(&cobalt_lock, flags); -} - -static struct irq_chip cobalt_irq_type = { -	.name		= "Cobalt-APIC", -	.irq_enable	= enable_cobalt_irq, -	.irq_disable	= disable_cobalt_irq, -	.irq_ack	= ack_cobalt_irq, -}; - - -/* - * This is the PIIX4-based 8259 that is wired up indirectly to Cobalt - * -- not the manner expected by the code in i8259.c. - * - * there is a 'master' physical interrupt source that gets sent to - * the CPU. But in the chipset there are various 'virtual' interrupts - * waiting to be handled. We represent this to Linux through a 'master' - * interrupt controller type, and through a special virtual interrupt- - * controller. Device drivers only see the virtual interrupt sources. - */ -static unsigned int startup_piix4_master_irq(struct irq_data *data) -{ -	legacy_pic->init(0); -	enable_cobalt_irq(data); -	return 0; -} - -static struct irq_chip piix4_master_irq_type = { -	.name		= "PIIX4-master", -	.irq_startup	= startup_piix4_master_irq, -	.irq_ack	= ack_cobalt_irq, -}; - -static void pii4_mask(struct irq_data *data) { } - -static struct irq_chip piix4_virtual_irq_type = { -	.name		= "PIIX4-virtual", -	.irq_mask	= pii4_mask, -}; - -/* - * PIIX4-8259 master/virtual functions to handle interrupt requests - * from legacy devices: floppy, parallel, serial, rtc. - * - * None of these get Cobalt APIC entries, neither do they have IDT - * entries. These interrupts are purely virtual and distributed from - * the 'master' interrupt source: CO_IRQ_8259. - * - * When the 8259 interrupts its handler figures out which of these - * devices is interrupting and dispatches to its handler. - * - * CAREFUL: devices see the 'virtual' interrupt only. Thus disable/ - * enable_irq gets the right irq. This 'master' irq is never directly - * manipulated by any driver. - */ -static irqreturn_t piix4_master_intr(int irq, void *dev_id) -{ -	unsigned long flags; -	int realirq; - -	raw_spin_lock_irqsave(&i8259A_lock, flags); - -	/* Find out what's interrupting in the PIIX4 master 8259 */ -	outb(0x0c, 0x20);		/* OCW3 Poll command */ -	realirq = inb(0x20); - -	/* -	 * Bit 7 == 0 means invalid/spurious -	 */ -	if (unlikely(!(realirq & 0x80))) -		goto out_unlock; - -	realirq &= 7; - -	if (unlikely(realirq == 2)) { -		outb(0x0c, 0xa0); -		realirq = inb(0xa0); - -		if (unlikely(!(realirq & 0x80))) -			goto out_unlock; - -		realirq = (realirq & 7) + 8; -	} - -	/* mask and ack interrupt */ -	cached_irq_mask |= 1 << realirq; -	if (unlikely(realirq > 7)) { -		inb(0xa1); -		outb(cached_slave_mask, 0xa1); -		outb(0x60 + (realirq & 7), 0xa0); -		outb(0x60 + 2, 0x20); -	} else { -		inb(0x21); -		outb(cached_master_mask, 0x21); -		outb(0x60 + realirq, 0x20); -	} - -	raw_spin_unlock_irqrestore(&i8259A_lock, flags); - -	/* -	 * handle this 'virtual interrupt' as a Cobalt one now. -	 */ -	generic_handle_irq(realirq); - -	return IRQ_HANDLED; - -out_unlock: -	raw_spin_unlock_irqrestore(&i8259A_lock, flags); -	return IRQ_NONE; -} - -static struct irqaction master_action = { -	.handler =	piix4_master_intr, -	.name =		"PIIX4-8259", -	.flags =	IRQF_NO_THREAD, -}; - -static struct irqaction cascade_action = { -	.handler = 	no_action, -	.name =		"cascade", -	.flags =	IRQF_NO_THREAD, -}; - -static inline void set_piix4_virtual_irq_type(void) -{ -	piix4_virtual_irq_type.irq_enable = i8259A_chip.irq_unmask; -	piix4_virtual_irq_type.irq_disable = i8259A_chip.irq_mask; -	piix4_virtual_irq_type.irq_unmask = i8259A_chip.irq_unmask; -} - -static void __init visws_pre_intr_init(void) -{ -	int i; - -	set_piix4_virtual_irq_type(); - -	for (i = 0; i < CO_IRQ_APIC0 + CO_APIC_LAST + 1; i++) { -		struct irq_chip *chip = NULL; - -		if (i == 0) -			chip = &cobalt_irq_type; -		else if (i == CO_IRQ_IDE0) -			chip = &cobalt_irq_type; -		else if (i == CO_IRQ_IDE1) -			chip = &cobalt_irq_type; -		else if (i == CO_IRQ_8259) -			chip = &piix4_master_irq_type; -		else if (i < CO_IRQ_APIC0) -			chip = &piix4_virtual_irq_type; -		else if (IS_CO_APIC(i)) -			chip = &cobalt_irq_type; - -		if (chip) -			irq_set_chip(i, chip); -	} - -	setup_irq(CO_IRQ_8259, &master_action); -	setup_irq(2, &cascade_action); -}  | 
