From 298b946e63911e81f78e2ed29541539b5db3268f Mon Sep 17 00:00:00 2001 From: Rebecca Schultz Zavin Date: Thu, 27 May 2010 12:18:57 -0700 Subject: [PATCH] [ARM] tegra: stingray: Add SPI board file Initial submission of Stingray SPI board file. Change-Id: Iab0968bff87bde4acaf9a46f3a35ec3e39243634 Signed-off-by: Greg Meiste --- arch/arm/mach-tegra/Makefile | 1 + arch/arm/mach-tegra/board-stingray-spi.c | 545 +++++++++++++++++++++++ arch/arm/mach-tegra/board-stingray.c | 1 + arch/arm/mach-tegra/board-stingray.h | 1 + 4 files changed, 548 insertions(+) create mode 100644 arch/arm/mach-tegra/board-stingray-spi.c diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index 30bdad11f216..88218081d66d 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -59,3 +59,4 @@ obj-${CONFIG_MACH_STINGRAY} += board-stingray-wifi.o obj-${CONFIG_MACH_STINGRAY} += board-stingray-sensors.o obj-${CONFIG_MACH_STINGRAY} += board-stingray-wlan_nvs.o obj-${CONFIG_MACH_STINGRAY} += board-stingray-touch.o +obj-${CONFIG_MACH_STINGRAY} += board-stingray-spi.o diff --git a/arch/arm/mach-tegra/board-stingray-spi.c b/arch/arm/mach-tegra/board-stingray-spi.c new file mode 100644 index 000000000000..fe26d5fa4d4b --- /dev/null +++ b/arch/arm/mach-tegra/board-stingray-spi.c @@ -0,0 +1,545 @@ +/* + * Copyright (C) 2010 Motorola, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "gpio-names.h" + +static struct cpcap_device *cpcap_di; + +static int cpcap_validity_reboot(struct notifier_block *this, + unsigned long code, void *cmd) +{ + int ret = -1; + int result = NOTIFY_DONE; + char *mode = cmd; + + dev_info(&(cpcap_di->spi->dev), "Saving power down reason.\n"); + + if (code == SYS_RESTART) { + if (mode != NULL && !strncmp("outofcharge", mode, 12)) { + /* Set the outofcharge bit in the cpcap */ + ret = cpcap_regacc_write(cpcap_di, CPCAP_REG_VAL1, + CPCAP_BIT_OUT_CHARGE_ONLY, + CPCAP_BIT_OUT_CHARGE_ONLY); + if (ret) { + dev_err(&(cpcap_di->spi->dev), + "outofcharge cpcap set failure.\n"); + result = NOTIFY_BAD; + } + /* Set the soft reset bit in the cpcap */ + cpcap_regacc_write(cpcap_di, CPCAP_REG_VAL1, + CPCAP_BIT_SOFT_RESET, + CPCAP_BIT_SOFT_RESET); + if (ret) { + dev_err(&(cpcap_di->spi->dev), + "reset cpcap set failure.\n"); + result = NOTIFY_BAD; + } + } + + /* Check if we are starting recovery mode */ + if (mode != NULL && !strncmp("recovery", mode, 9)) { + /* Set the fota (recovery mode) bit in the cpcap */ + ret = cpcap_regacc_write(cpcap_di, CPCAP_REG_VAL1, + CPCAP_BIT_FOTA_MODE, CPCAP_BIT_FOTA_MODE); + if (ret) { + dev_err(&(cpcap_di->spi->dev), + "Recovery cpcap set failure.\n"); + result = NOTIFY_BAD; + } + } else { + /* Set the fota (recovery mode) bit in the cpcap */ + ret = cpcap_regacc_write(cpcap_di, CPCAP_REG_VAL1, 0, + CPCAP_BIT_FOTA_MODE); + if (ret) { + dev_err(&(cpcap_di->spi->dev), + "Recovery cpcap clear failure.\n"); + result = NOTIFY_BAD; + } + } + /* Check if we are going into fast boot mode */ + if (mode != NULL && !strncmp("bootloader", mode, 11)) { + /* Set the bootmode bit in the cpcap */ + ret = cpcap_regacc_write(cpcap_di, CPCAP_REG_VAL1, + CPCAP_BIT_BOOT_MODE, CPCAP_BIT_BOOT_MODE); + if (ret) { + dev_err(&(cpcap_di->spi->dev), + "Boot mode cpcap set failure.\n"); + result = NOTIFY_BAD; + } + } + } else { + ret = cpcap_regacc_write(cpcap_di, CPCAP_REG_VAL1, + 0, + CPCAP_BIT_OUT_CHARGE_ONLY); + if (ret) { + dev_err(&(cpcap_di->spi->dev), + "outofcharge cpcap set failure.\n"); + result = NOTIFY_BAD; + } + + /* Clear the soft reset bit in the cpcap */ + ret = cpcap_regacc_write(cpcap_di, CPCAP_REG_VAL1, 0, + CPCAP_BIT_SOFT_RESET); + if (ret) { + dev_err(&(cpcap_di->spi->dev), + "SW Reset cpcap set failure.\n"); + result = NOTIFY_BAD; + } + /* Clear the fota (recovery mode) bit in the cpcap */ + ret = cpcap_regacc_write(cpcap_di, CPCAP_REG_VAL1, 0, + CPCAP_BIT_FOTA_MODE); + if (ret) { + dev_err(&(cpcap_di->spi->dev), + "Recovery cpcap clear failure.\n"); + result = NOTIFY_BAD; + } + } + + /* Always clear the kpanic bit */ + ret = cpcap_regacc_write(cpcap_di, CPCAP_REG_VAL1, + 0, CPCAP_BIT_AP_KERNEL_PANIC); + if (ret) { + dev_err(&(cpcap_di->spi->dev), + "Clear kernel panic bit failure.\n"); + result = NOTIFY_BAD; + } + + return result; +} +static struct notifier_block validity_reboot_notifier = { + .notifier_call = cpcap_validity_reboot, +}; + +static int cpcap_validity_probe(struct platform_device *pdev) +{ + if (pdev->dev.platform_data == NULL) { + dev_err(&pdev->dev, "no platform_data\n"); + return -EINVAL; + } + + cpcap_di = pdev->dev.platform_data; + + cpcap_regacc_write(cpcap_di, CPCAP_REG_VAL1, + (CPCAP_BIT_AP_KERNEL_PANIC | CPCAP_BIT_SOFT_RESET), + (CPCAP_BIT_AP_KERNEL_PANIC | CPCAP_BIT_SOFT_RESET)); + + register_reboot_notifier(&validity_reboot_notifier); + + return 0; +} + +static int cpcap_validity_remove(struct platform_device *pdev) +{ + unregister_reboot_notifier(&validity_reboot_notifier); + cpcap_di = NULL; + + return 0; +} + +static struct platform_driver cpcap_validity_driver = { + .probe = cpcap_validity_probe, + .remove = cpcap_validity_remove, + .driver = { + .name = "cpcap_validity", + .owner = THIS_MODULE, + }, +}; + +static struct platform_device cpcap_validity_device = { + .name = "cpcap_validity", + .id = -1, + .dev = { + .platform_data = NULL, + }, +}; + +static struct platform_device cpcap_3mm5_device = { + .name = "cpcap_3mm5", + .id = -1, + .dev = { + .platform_data = NULL, + }, +}; + +static struct cpcap_whisper_pdata whisper_pdata = { + .gpio = TEGRA_GPIO_PV4, + .uartmux = 1, +}; + +static struct platform_device cpcap_whisper_device = { + .name = "cpcap_whisper", + .id = -1, + .dev = { + .platform_data = &whisper_pdata, + }, +}; + +static struct platform_device *cpcap_devices[] = { + &cpcap_validity_device, + &cpcap_whisper_device, + &cpcap_3mm5_device, +}; + +struct cpcap_spi_init_data stingray_cpcap_spi_init[] = { + {CPCAP_REG_ADCC1, 0x9000}, + {CPCAP_REG_ADCC2, 0x4136}, + {CPCAP_REG_USBC1, 0x1201}, + {CPCAP_REG_USBC3, 0x7DFB}, + {CPCAP_REG_OWDC, 0x0003}, +}; + +unsigned short cpcap_regulator_mode_values[CPCAP_NUM_REGULATORS] = { + [CPCAP_SW5] = 0x0022, + [CPCAP_VCAM] = 0x0003, + [CPCAP_VCSI] = 0x0003, + [CPCAP_VDAC] = 0x0003, + [CPCAP_VDIG] = 0x0003, + [CPCAP_VFUSE] = 0x0080, + [CPCAP_VHVIO] = 0x0003, + [CPCAP_VSDIO] = 0x0003, + [CPCAP_VPLL] = 0x0042, + [CPCAP_VRF1] = 0x000C, + [CPCAP_VRF2] = 0x0003, + [CPCAP_VRFREF] = 0x0003, + [CPCAP_VWLAN1] = 0x0003, + [CPCAP_VWLAN2] = 0x000C, + [CPCAP_VSIM] = 0x0003, + [CPCAP_VSIMCARD] = 0x1E00, + [CPCAP_VVIB] = 0x0001, + [CPCAP_VUSB] = 0x000C, + [CPCAP_VAUDIO] = 0x0006, +}; + +unsigned short cpcap_regulator_off_mode_values[CPCAP_NUM_REGULATORS] = { + [CPCAP_SW5] = 0x0000, + [CPCAP_VCAM] = 0x0000, + [CPCAP_VCSI] = 0x0000, + [CPCAP_VDAC] = 0x0000, + [CPCAP_VDIG] = 0x0000, + [CPCAP_VFUSE] = 0x0000, + [CPCAP_VHVIO] = 0x0000, + [CPCAP_VSDIO] = 0x0000, + [CPCAP_VPLL] = 0x0000, + [CPCAP_VRF1] = 0x0000, + [CPCAP_VRF2] = 0x0000, + [CPCAP_VRFREF] = 0x0000, + [CPCAP_VWLAN1] = 0x0000, + [CPCAP_VWLAN2] = 0x0000, + [CPCAP_VSIM] = 0x0000, + [CPCAP_VSIMCARD] = 0x0000, + [CPCAP_VVIB] = 0x0000, + [CPCAP_VUSB] = 0x0000, + [CPCAP_VAUDIO] = 0x0000, +}; + +#define REGULATOR_CONSUMER(name, device) { .supply = name, .dev = device, } + +struct regulator_consumer_supply cpcap_sw5_consumers[] = { + REGULATOR_CONSUMER("sw5", NULL), +}; + +struct regulator_consumer_supply cpcap_vcam_consumers[] = { + REGULATOR_CONSUMER("vcam", NULL /* cpcap_cam_device */), +}; + +struct regulator_consumer_supply cpcap_vhvio_consumers[] = { + REGULATOR_CONSUMER("vhvio", NULL /* lighting_driver */), +#if 0 + REGULATOR_CONSUMER("vhvio", NULL /* lighting_driver */), + REGULATOR_CONSUMER("vhvio", NULL /* magnetometer */), + REGULATOR_CONSUMER("vhvio", NULL /* light sensor */), + REGULATOR_CONSUMER("vhvio", NULL /* accelerometer */), + REGULATOR_CONSUMER("vhvio", NULL /* display */), +#endif +}; + +struct regulator_consumer_supply cpcap_vsdio_consumers[] = { + REGULATOR_CONSUMER("vsdio", NULL), +}; + +struct regulator_consumer_supply cpcap_vcsi_consumers[] = { + REGULATOR_CONSUMER("vcsi", NULL), +}; + +struct regulator_consumer_supply cpcap_vwlan2_consumers[] = { + REGULATOR_CONSUMER("vwlan2", NULL /* sd slot */), +}; + +struct regulator_consumer_supply cpcap_vvib_consumers[] = { + REGULATOR_CONSUMER("vvib", NULL /* vibrator */), +}; + +struct regulator_consumer_supply cpcap_vusb_consumers[] = { + REGULATOR_CONSUMER("vusb", &cpcap_whisper_device.dev), +}; + +struct regulator_consumer_supply cpcap_vaudio_consumers[] = { + REGULATOR_CONSUMER("vaudio", NULL /* mic opamp */), +}; + +static struct regulator_init_data cpcap_regulator[CPCAP_NUM_REGULATORS] = { + [CPCAP_SW5] = { + .constraints = { + .min_uV = 5050000, + .max_uV = 5050000, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .apply_uV = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(cpcap_sw5_consumers), + .consumer_supplies = cpcap_sw5_consumers, + }, + [CPCAP_VCAM] = { + .constraints = { + .min_uV = 2900000, + .max_uV = 2900000, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .apply_uV = 1, + + }, + .num_consumer_supplies = ARRAY_SIZE(cpcap_vcam_consumers), + .consumer_supplies = cpcap_vcam_consumers, + }, + [CPCAP_VCSI] = { + .constraints = { + .min_uV = 1200000, + .max_uV = 1200000, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .boot_on = 1, + .apply_uV = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(cpcap_vcsi_consumers), + .consumer_supplies = cpcap_vcsi_consumers, + }, + [CPCAP_VDAC] = { + .constraints = { + .min_uV = 1800000, + .max_uV = 1800000, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .apply_uV = 1, + }, + }, + [CPCAP_VDIG] = { + .constraints = { + .min_uV = 1200000, + .max_uV = 1875000, + .valid_ops_mask = 0, + }, + }, + [CPCAP_VFUSE] = { + .constraints = { + .min_uV = 1500000, + .max_uV = 3150000, + .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_STATUS), + }, + }, + [CPCAP_VHVIO] = { + .constraints = { + .min_uV = 2775000, + .max_uV = 2775000, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .always_on = 1, + .apply_uV = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(cpcap_vhvio_consumers), + .consumer_supplies = cpcap_vhvio_consumers, + }, + [CPCAP_VSDIO] = { + .constraints = { + .min_uV = 3000000, + .max_uV = 3000000, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .apply_uV = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(cpcap_vsdio_consumers), + .consumer_supplies = cpcap_vsdio_consumers, + }, + [CPCAP_VPLL] = { + .constraints = { + .min_uV = 1800000, + .max_uV = 1800000, + .valid_ops_mask = 0, + .always_on = 1, + .apply_uV = 1, + }, + }, + [CPCAP_VRF1] = { + .constraints = { + .min_uV = 2500000, + .max_uV = 2775000, + .valid_ops_mask = 0, + }, + }, + [CPCAP_VRF2] = { + .constraints = { + .min_uV = 2775000, + .max_uV = 2775000, + .valid_ops_mask = 0, + }, + }, + [CPCAP_VRFREF] = { + .constraints = { + .min_uV = 2500000, + .max_uV = 2775000, + .valid_ops_mask = 0, + }, + }, + [CPCAP_VWLAN1] = { + .constraints = { + .min_uV = 1800000, + .max_uV = 1900000, + .valid_ops_mask = 0, + }, + }, + [CPCAP_VWLAN2] = { + .constraints = { + .min_uV = 3300000, + .max_uV = 3300000, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .apply_uV = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(cpcap_vwlan2_consumers), + .consumer_supplies = cpcap_vwlan2_consumers, + }, + [CPCAP_VSIM] = { + .constraints = { + .min_uV = 1800000, + .max_uV = 2900000, + .valid_ops_mask = 0, + }, + }, + [CPCAP_VSIMCARD] = { + .constraints = { + .min_uV = 1800000, + .max_uV = 2900000, + .valid_ops_mask = 0, + }, + }, + [CPCAP_VVIB] = { + .constraints = { + .min_uV = 1300000, + .max_uV = 3000000, + .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_STATUS), + }, + .num_consumer_supplies = ARRAY_SIZE(cpcap_vvib_consumers), + .consumer_supplies = cpcap_vvib_consumers, + }, + [CPCAP_VUSB] = { + .constraints = { + .min_uV = 3300000, + .max_uV = 3300000, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .apply_uV = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(cpcap_vusb_consumers), + .consumer_supplies = cpcap_vusb_consumers, + }, + [CPCAP_VAUDIO] = { + .constraints = { + .min_uV = 2775000, + .max_uV = 2775000, + .valid_modes_mask = (REGULATOR_MODE_NORMAL | + REGULATOR_MODE_STANDBY), + .valid_ops_mask = REGULATOR_CHANGE_MODE, + .always_on = 1, + .apply_uV = 1, + }, + .num_consumer_supplies = ARRAY_SIZE(cpcap_vaudio_consumers), + .consumer_supplies = cpcap_vaudio_consumers, + }, +}; + +static struct cpcap_adc_ato stingray_cpcap_adc_ato = { + .ato_in = 0x0480, + .atox_in = 0, + .adc_ps_factor_in = 0x0200, + .atox_ps_factor_in = 0, + .ato_out = 0, + .atox_out = 0, + .adc_ps_factor_out = 0, + .atox_ps_factor_out = 0, +}; + +static struct cpcap_leds stingray_cpcap_leds = { + .button_led = { + .button_reg = CPCAP_REG_KLC, + .button_mask = 0x03FF, + .button_on = 0x00F5, + .button_off = 0x00F4, + }, + .rgb_led = { + .rgb_on = 0x0053, + }, +}; + +static struct cpcap_platform_data stingray_cpcap_data = { + .init = stingray_cpcap_spi_init, + .init_len = ARRAY_SIZE(stingray_cpcap_spi_init), + .regulator_mode_values = cpcap_regulator_mode_values, + .regulator_off_mode_values = cpcap_regulator_off_mode_values, + .regulator_init = cpcap_regulator, + .adc_ato = &stingray_cpcap_adc_ato, + .leds = &stingray_cpcap_leds, + .ac_changed = NULL, + .batt_changed = NULL, + .usb_changed = NULL, +}; + +static struct spi_board_info stingray_spi_board_info[] __initdata = { + { + .modalias = "cpcap", + .bus_num = 1, + .chip_select = 0, + .mode = SPI_MODE_0, + .max_speed_hz = 10000000, + .controller_data = &stingray_cpcap_data, + .irq = INT_EXTERNAL_PMU, + }, +}; + +int __init stingray_spi_init(void) +{ + int i; + + spi_register_board_info(stingray_spi_board_info, + ARRAY_SIZE(stingray_spi_board_info)); + + for (i = 0; i < ARRAY_SIZE(cpcap_devices); i++) + cpcap_device_register(cpcap_devices[i]); + + (void) cpcap_driver_register(&cpcap_validity_driver); + + return 0; +} diff --git a/arch/arm/mach-tegra/board-stingray.c b/arch/arm/mach-tegra/board-stingray.c index 61a8757aaae9..40ceb036b5d9 100644 --- a/arch/arm/mach-tegra/board-stingray.c +++ b/arch/arm/mach-tegra/board-stingray.c @@ -302,6 +302,7 @@ static void __init tegra_stingray_init(void) stingray_keypad_init(); stingray_touch_init(); + stingray_spi_init(); stingray_panel_init(); stingray_sdhci_init(); stingray_sensors_init(); diff --git a/arch/arm/mach-tegra/board-stingray.h b/arch/arm/mach-tegra/board-stingray.h index 50979eb70195..336f7ba40b97 100644 --- a/arch/arm/mach-tegra/board-stingray.h +++ b/arch/arm/mach-tegra/board-stingray.h @@ -24,5 +24,6 @@ int stingray_i2c_init(void); int stingray_wlan_init(void); int stingray_sensors_init(void); int stingray_touch_init(void); +int stingray_spi_init(void); #endif -- 2.34.1