diff options
Diffstat (limited to 'arch/x86/platform/intel-mid')
30 files changed, 2463 insertions, 0 deletions
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/intel-mid/early_printk_intel_mid.c b/arch/x86/platform/intel-mid/early_printk_intel_mid.c new file mode 100644 index 00000000000..e0bd082a80e --- /dev/null +++ b/arch/x86/platform/intel-mid/early_printk_intel_mid.c @@ -0,0 +1,324 @@ +/* + * early_printk_intel_mid.c - early consoles for Intel MID platforms + * + * Copyright (c) 2008-2010, 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. + */ + +/* + * This file implements two early consoles named mrst and hsu. + * mrst is based on Maxim3110 spi-uart device, it exists in both + * Moorestown and Medfield platforms, while hsu is based on a High + * Speed UART device which only exists in the Medfield platform + */ + +#include <linux/serial_reg.h> +#include <linux/serial_mfd.h> +#include <linux/kmsg_dump.h> +#include <linux/console.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/io.h> + +#include <asm/fixmap.h> +#include <asm/pgtable.h> +#include <asm/intel-mid.h> + +#define MRST_SPI_TIMEOUT		0x200000 +#define MRST_REGBASE_SPI0		0xff128000 +#define MRST_REGBASE_SPI1		0xff128400 +#define MRST_CLK_SPI0_REG		0xff11d86c + +/* Bit fields in CTRLR0 */ +#define SPI_DFS_OFFSET			0 + +#define SPI_FRF_OFFSET			4 +#define SPI_FRF_SPI			0x0 +#define SPI_FRF_SSP			0x1 +#define SPI_FRF_MICROWIRE		0x2 +#define SPI_FRF_RESV			0x3 + +#define SPI_MODE_OFFSET			6 +#define SPI_SCPH_OFFSET			6 +#define SPI_SCOL_OFFSET			7 +#define SPI_TMOD_OFFSET			8 +#define	SPI_TMOD_TR			0x0		/* xmit & recv */ +#define SPI_TMOD_TO			0x1		/* xmit only */ +#define SPI_TMOD_RO			0x2		/* recv only */ +#define SPI_TMOD_EPROMREAD		0x3		/* eeprom read mode */ + +#define SPI_SLVOE_OFFSET		10 +#define SPI_SRL_OFFSET			11 +#define SPI_CFS_OFFSET			12 + +/* Bit fields in SR, 7 bits */ +#define SR_MASK				0x7f		/* cover 7 bits */ +#define SR_BUSY				(1 << 0) +#define SR_TF_NOT_FULL			(1 << 1) +#define SR_TF_EMPT			(1 << 2) +#define SR_RF_NOT_EMPT			(1 << 3) +#define SR_RF_FULL			(1 << 4) +#define SR_TX_ERR			(1 << 5) +#define SR_DCOL				(1 << 6) + +struct dw_spi_reg { +	u32	ctrl0; +	u32	ctrl1; +	u32	ssienr; +	u32	mwcr; +	u32	ser; +	u32	baudr; +	u32	txfltr; +	u32	rxfltr; +	u32	txflr; +	u32	rxflr; +	u32	sr; +	u32	imr; +	u32	isr; +	u32	risr; +	u32	txoicr; +	u32	rxoicr; +	u32	rxuicr; +	u32	msticr; +	u32	icr; +	u32	dmacr; +	u32	dmatdlr; +	u32	dmardlr; +	u32	idr; +	u32	version; + +	/* Currently operates as 32 bits, though only the low 16 bits matter */ +	u32	dr; +} __packed; + +#define dw_readl(dw, name)		__raw_readl(&(dw)->name) +#define dw_writel(dw, name, val)	__raw_writel((val), &(dw)->name) + +/* Default use SPI0 register for mrst, we will detect Penwell and use SPI1 */ +static unsigned long mrst_spi_paddr = MRST_REGBASE_SPI0; + +static u32 *pclk_spi0; +/* Always contains an accessible address, start with 0 */ +static struct dw_spi_reg *pspi; + +static struct kmsg_dumper dw_dumper; +static int dumper_registered; + +static void dw_kmsg_dump(struct kmsg_dumper *dumper, +			 enum kmsg_dump_reason reason) +{ +	static char line[1024]; +	size_t len; + +	/* When run to this, we'd better re-init the HW */ +	mrst_early_console_init(); + +	while (kmsg_dump_get_line(dumper, true, line, sizeof(line), &len)) +		early_mrst_console.write(&early_mrst_console, line, len); +} + +/* Set the ratio rate to 115200, 8n1, IRQ disabled */ +static void max3110_write_config(void) +{ +	u16 config; + +	config = 0xc001; +	dw_writel(pspi, dr, config); +} + +/* Translate char to a eligible word and send to max3110 */ +static void max3110_write_data(char c) +{ +	u16 data; + +	data = 0x8000 | c; +	dw_writel(pspi, dr, data); +} + +void mrst_early_console_init(void) +{ +	u32 ctrlr0 = 0; +	u32 spi0_cdiv; +	u32 freq; /* Freqency info only need be searched once */ + +	/* Base clk is 100 MHz, the actual clk = 100M / (clk_divider + 1) */ +	pclk_spi0 = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, +							MRST_CLK_SPI0_REG); +	spi0_cdiv = ((*pclk_spi0) & 0xe00) >> 9; +	freq = 100000000 / (spi0_cdiv + 1); + +	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, +						mrst_spi_paddr); + +	/* Disable SPI controller */ +	dw_writel(pspi, ssienr, 0); + +	/* Set control param, 8 bits, transmit only mode */ +	ctrlr0 = dw_readl(pspi, ctrl0); + +	ctrlr0 &= 0xfcc0; +	ctrlr0 |= 0xf | (SPI_FRF_SPI << SPI_FRF_OFFSET) +		      | (SPI_TMOD_TO << SPI_TMOD_OFFSET); +	dw_writel(pspi, ctrl0, ctrlr0); + +	/* +	 * Change the spi0 clk to comply with 115200 bps, use 100000 to +	 * calculate the clk dividor to make the clock a little slower +	 * than real baud rate. +	 */ +	dw_writel(pspi, baudr, freq/100000); + +	/* Disable all INT for early phase */ +	dw_writel(pspi, imr, 0x0); + +	/* Set the cs to spi-uart */ +	dw_writel(pspi, ser, 0x2); + +	/* Enable the HW, the last step for HW init */ +	dw_writel(pspi, ssienr, 0x1); + +	/* Set the default configuration */ +	max3110_write_config(); + +	/* Register the kmsg dumper */ +	if (!dumper_registered) { +		dw_dumper.dump = dw_kmsg_dump; +		kmsg_dump_register(&dw_dumper); +		dumper_registered = 1; +	} +} + +/* Slave select should be called in the read/write function */ +static void early_mrst_spi_putc(char c) +{ +	unsigned int timeout; +	u32 sr; + +	timeout = MRST_SPI_TIMEOUT; +	/* Early putc needs to make sure the TX FIFO is not full */ +	while (--timeout) { +		sr = dw_readl(pspi, sr); +		if (!(sr & SR_TF_NOT_FULL)) +			cpu_relax(); +		else +			break; +	} + +	if (!timeout) +		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) +{ +	int i; + +	for (i = 0; i < n && *str; i++) { +		if (*str == '\n') +			early_mrst_spi_putc('\r'); +		early_mrst_spi_putc(*str); +		str++; +	} +} + +struct console early_mrst_console = { +	.name =		"earlymrst", +	.write =	early_mrst_spi_write, +	.flags =	CON_PRINTBUFFER, +	.index =	-1, +}; + +/* + * Following is the early console based on Medfield HSU (High + * Speed UART) device. + */ +#define HSU_PORT_BASE		0xffa28080 + +static void __iomem *phsu; + +void hsu_early_console_init(const char *s) +{ +	unsigned long paddr, port = 0; +	u8 lcr; + +	/* +	 * Select the early HSU console port if specified by user in the +	 * kernel command line. +	 */ +	if (*s && !kstrtoul(s, 10, &port)) +		port = clamp_val(port, 0, 2); + +	paddr = HSU_PORT_BASE + port * 0x80; +	phsu = (void *)set_fixmap_offset_nocache(FIX_EARLYCON_MEM_BASE, paddr); + +	/* Disable FIFO */ +	writeb(0x0, phsu + UART_FCR); + +	/* Set to default 115200 bps, 8n1 */ +	lcr = readb(phsu + UART_LCR); +	writeb((0x80 | lcr), phsu + UART_LCR); +	writeb(0x18, phsu + UART_DLL); +	writeb(lcr,  phsu + UART_LCR); +	writel(0x3600, phsu + UART_MUL*4); + +	writeb(0x8, phsu + UART_MCR); +	writeb(0x7, phsu + UART_FCR); +	writeb(0x3, phsu + UART_LCR); + +	/* Clear IRQ status */ +	readb(phsu + UART_LSR); +	readb(phsu + UART_RX); +	readb(phsu + UART_IIR); +	readb(phsu + UART_MSR); + +	/* Enable FIFO */ +	writeb(0x7, phsu + UART_FCR); +} + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static void early_hsu_putc(char ch) +{ +	unsigned int timeout = 10000; /* 10ms */ +	u8 status; + +	while (--timeout) { +		status = readb(phsu + UART_LSR); +		if (status & BOTH_EMPTY) +			break; +		udelay(1); +	} + +	/* Only write the char when there was no timeout */ +	if (timeout) +		writeb(ch, phsu + UART_TX); +} + +static void early_hsu_write(struct console *con, const char *str, unsigned n) +{ +	int i; + +	for (i = 0; i < n && *str; i++) { +		if (*str == '\n') +			early_hsu_putc('\r'); +		early_hsu_putc(*str); +		str++; +	} +} + +struct console early_hsu_console = { +	.name =		"earlyhsu", +	.write =	early_hsu_write, +	.flags =	CON_PRINTBUFFER, +	.index =	-1, +}; 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/intel-mid/intel_mid_vrtc.c b/arch/x86/platform/intel-mid/intel_mid_vrtc.c new file mode 100644 index 00000000000..4762cff7fac --- /dev/null +++ b/arch/x86/platform/intel-mid/intel_mid_vrtc.c @@ -0,0 +1,177 @@ +/* + * intel_mid_vrtc.c: Driver for virtual RTC device on Intel MID platform + * + * (C) Copyright 2009 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. + * + * Note: + * VRTC is emulated by system controller firmware, the real HW + * RTC is located in the PMIC device. SCU FW shadows PMIC RTC + * in a memory mapped IO space that is visible to the host IA + * processor. + * + * This driver is based on RTC CMOS driver. + */ + +#include <linux/kernel.h> +#include <linux/export.h> +#include <linux/init.h> +#include <linux/sfi.h> +#include <linux/platform_device.h> + +#include <asm/intel-mid.h> +#include <asm/intel_mid_vrtc.h> +#include <asm/time.h> +#include <asm/fixmap.h> + +static unsigned char __iomem *vrtc_virt_base; + +unsigned char vrtc_cmos_read(unsigned char reg) +{ +	unsigned char retval; + +	/* vRTC's registers range from 0x0 to 0xD */ +	if (reg > 0xd || !vrtc_virt_base) +		return 0xff; + +	lock_cmos_prefix(reg); +	retval = __raw_readb(vrtc_virt_base + (reg << 2)); +	lock_cmos_suffix(reg); +	return retval; +} +EXPORT_SYMBOL_GPL(vrtc_cmos_read); + +void vrtc_cmos_write(unsigned char val, unsigned char reg) +{ +	if (reg > 0xd || !vrtc_virt_base) +		return; + +	lock_cmos_prefix(reg); +	__raw_writeb(val, vrtc_virt_base + (reg << 2)); +	lock_cmos_suffix(reg); +} +EXPORT_SYMBOL_GPL(vrtc_cmos_write); + +void vrtc_get_time(struct timespec *now) +{ +	u8 sec, min, hour, mday, mon; +	unsigned long flags; +	u32 year; + +	spin_lock_irqsave(&rtc_lock, flags); + +	while ((vrtc_cmos_read(RTC_FREQ_SELECT) & RTC_UIP)) +		cpu_relax(); + +	sec = vrtc_cmos_read(RTC_SECONDS); +	min = vrtc_cmos_read(RTC_MINUTES); +	hour = vrtc_cmos_read(RTC_HOURS); +	mday = vrtc_cmos_read(RTC_DAY_OF_MONTH); +	mon = vrtc_cmos_read(RTC_MONTH); +	year = vrtc_cmos_read(RTC_YEAR); + +	spin_unlock_irqrestore(&rtc_lock, flags); + +	/* vRTC YEAR reg contains the offset to 1972 */ +	year += 1972; + +	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); +	now->tv_nsec = 0; +} + +int vrtc_set_mmss(const struct timespec *now) +{ +	unsigned long flags; +	struct rtc_time tm; +	int year; +	int retval = 0; + +	rtc_time_to_tm(now->tv_sec, &tm); +	if (!rtc_valid_tm(&tm) && tm.tm_year >= 72) { +		/* +		 * tm.year is the number of years since 1900, and the +		 * vrtc need the years since 1972. +		 */ +		year = tm.tm_year - 72; +		spin_lock_irqsave(&rtc_lock, flags); +		vrtc_cmos_write(year, RTC_YEAR); +		vrtc_cmos_write(tm.tm_mon, RTC_MONTH); +		vrtc_cmos_write(tm.tm_mday, RTC_DAY_OF_MONTH); +		vrtc_cmos_write(tm.tm_hour, RTC_HOURS); +		vrtc_cmos_write(tm.tm_min, RTC_MINUTES); +		vrtc_cmos_write(tm.tm_sec, RTC_SECONDS); +		spin_unlock_irqrestore(&rtc_lock, flags); +	} else { +		pr_err("%s: Invalid vRTC value: write of %lx to vRTC failed\n", +			__FUNCTION__, now->tv_sec); +		retval = -EINVAL; +	} +	return retval; +} + +void __init intel_mid_rtc_init(void) +{ +	unsigned long vrtc_paddr; + +	sfi_table_parse(SFI_SIG_MRTC, NULL, NULL, sfi_parse_mrtc); + +	vrtc_paddr = sfi_mrtc_array[0].phys_addr; +	if (!sfi_mrtc_num || !vrtc_paddr) +		return; + +	vrtc_virt_base = (void __iomem *)set_fixmap_offset_nocache(FIX_LNW_VRTC, +								vrtc_paddr); +	x86_platform.get_wallclock = vrtc_get_time; +	x86_platform.set_wallclock = vrtc_set_mmss; +} + +/* + * The Moorestown platform has a memory mapped virtual RTC device that emulates + * the programming interface of the RTC. + */ + +static struct resource vrtc_resources[] = { +	[0] = { +		.flags	= IORESOURCE_MEM, +	}, +	[1] = { +		.flags	= IORESOURCE_IRQ, +	} +}; + +static struct platform_device vrtc_device = { +	.name		= "rtc_mrst", +	.id		= -1, +	.resource	= vrtc_resources, +	.num_resources	= ARRAY_SIZE(vrtc_resources), +}; + +/* Register the RTC device if appropriate */ +static int __init intel_mid_device_create(void) +{ +	/* No Moorestown, no device */ +	if (!intel_mid_identify_cpu()) +		return -ENODEV; +	/* No timer, no device */ +	if (!sfi_mrtc_num) +		return -ENODEV; + +	/* iomem resource */ +	vrtc_resources[0].start = sfi_mrtc_array[0].phys_addr; +	vrtc_resources[0].end = sfi_mrtc_array[0].phys_addr + +				MRST_VRTC_MAP_SZ; +	/* irq resource */ +	vrtc_resources[1].start = sfi_mrtc_array[0].irq; +	vrtc_resources[1].end = sfi_mrtc_array[0].irq; + +	return platform_device_register(&vrtc_device); +} + +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);  | 
