diff options
Diffstat (limited to 'arch/arm/mach-tegra/board-apalis_t30.c')
-rw-r--r-- | arch/arm/mach-tegra/board-apalis_t30.c | 1196 |
1 files changed, 1196 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/board-apalis_t30.c b/arch/arm/mach-tegra/board-apalis_t30.c new file mode 100644 index 000000000000..5501c75f1eef --- /dev/null +++ b/arch/arm/mach-tegra/board-apalis_t30.c @@ -0,0 +1,1196 @@ +/* + * arch/arm/mach-tegra/board-apalis_t30.c + * + * Copyright (c) 2013 Toradex, Inc. + * + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. + */ + +#include <asm/mach/arch.h> +#include <asm/mach-types.h> + +#include <linux/can/platform/mcp251x.h> +#include <linux/clk.h> +#include <linux/types.h> /* required by linux/gpio_keys.h */ +#include <linux/gpio_keys.h> +#include <linux/i2c.h> +#include <linux/i2c-tegra.h> +#include <linux/input.h> +#include <linux/io.h> +#include <linux/leds.h> +#include <linux/leds_pwm.h> +#include <linux/lm95245.h> +#include <linux/mfd/stmpe.h> +#include <linux/platform_data/tegra_usb.h> +#include <linux/platform_device.h> +#include <linux/serial_8250.h> +#include <linux/spi/spi.h> +#include <linux/spi-tegra.h> +#include <linux/tegra_uart.h> + +#include <mach/io_dpd.h> +#include <mach/pci.h> +#include <mach/sdhci.h> +#include <mach/tegra_asoc_pdata.h> +#include <mach/tegra_fiq_debugger.h> +#include <mach/thermal.h> +#include <mach/usb_phy.h> +#include <mach/w1.h> + +#include "board-apalis_t30.h" +#include "board.h" +#include "clock.h" +#include "devices.h" +#include "gpio-names.h" +#include "pm.h" + +/* ADC */ + +//TODO + +/* Audio */ + +/* HDA */ + +//TODO + +/* I2S */ + +static struct tegra_asoc_platform_data apalis_t30_audio_sgtl5000_pdata = { + .gpio_spkr_en = -1, + .gpio_hp_det = -1, + .gpio_hp_mute = -1, + .gpio_int_mic_en = -1, + .gpio_ext_mic_en = -1, + .i2s_param[HIFI_CODEC] = { + .audio_port_id = 2, + .i2s_mode = TEGRA_DAIFMT_I2S, + .is_i2s_master = 1, + .sample_size = 16, + }, + .i2s_param[BASEBAND] = { + .audio_port_id = -1, + }, + .i2s_param[BT_SCO] = { + .audio_port_id = -1, + }, +}; + +static struct platform_device apalis_t30_audio_sgtl5000_device = { + .name = "tegra-snd-colibri_t30-sgtl5000", + .id = 0, + .dev = { + .platform_data = &apalis_t30_audio_sgtl5000_pdata, + }, +}; + +#ifdef CONFIG_TEGRA_CAMERA +/* Camera */ +static struct platform_device tegra_camera = { + .name = "tegra_camera", + .id = -1, +}; +#endif /* CONFIG_TEGRA_CAMERA */ + +/* CAN */ + +#if defined(CONFIG_CAN_MCP251X) || defined(CONFIG_CAN_MCP251X_MODULE) +static struct mcp251x_platform_data can_pdata = { + .oscillator_frequency = 16000000, + .power_enable = NULL, + .transceiver_enable = NULL +}; + +static struct spi_board_info can_board_info[] = { + { + .bus_num = 1, /* SPI2: CAN1 */ + .chip_select = 0, + .max_speed_hz = 10000000, + .modalias = "mcp2515", + .platform_data = &can_pdata, + }, + { + .bus_num = 3, /* SPI4: CAN2 */ + .chip_select = 1, + .max_speed_hz = 10000000, + .modalias = "mcp2515", + .platform_data = &can_pdata, + }, +}; + +static void __init apalis_t20_mcp2515_can_init(void) +{ + can_board_info[0].irq = gpio_to_irq(CAN1_INT); + can_board_info[1].irq = gpio_to_irq(CAN2_INT); + spi_register_board_info(can_board_info, ARRAY_SIZE(can_board_info)); +} +#else /* CONFIG_CAN_MCP251X | CONFIG_CAN_MCP251X_MODULE */ +#define apalis_t20_mcp2515_can_init() do {} while (0) +#endif /* CONFIG_CAN_MCP251X | CONFIG_CAN_MCP251X_MODULE */ + +/* CEC */ + +//TODO + +/* Clocks */ +static struct tegra_clk_init_table apalis_t30_clk_init_table[] __initdata = { + /* name parent rate enabled */ + {"audio1", "i2s1_sync", 0, false}, + {"audio2", "i2s2_sync", 0, false}, + {"audio3", "i2s3_sync", 0, false}, + {"blink", "clk_32k", 32768, true}, + {"d_audio", "clk_m", 12000000, false}, + {"dam0", "clk_m", 12000000, false}, + {"dam1", "clk_m", 12000000, false}, + {"dam2", "clk_m", 12000000, false}, + +//required? + {"hda", "pll_p", 108000000, false}, + {"hda2codec_2x","pll_p", 48000000, false}, + + {"i2c1", "pll_p", 3200000, false}, + {"i2c2", "pll_p", 3200000, false}, + {"i2c3", "pll_p", 3200000, false}, + {"i2c4", "pll_p", 3200000, false}, + {"i2c5", "pll_p", 3200000, false}, + {"i2s0", "pll_a_out0", 0, false}, + {"i2s1", "pll_a_out0", 0, false}, + {"i2s2", "pll_a_out0", 0, false}, + {"i2s3", "pll_a_out0", 0, false}, + {"pll_m", NULL, 0, false}, + {"pwm", "pll_p", 3187500, false}, + {"spdif_out", "pll_a_out0", 0, false}, + {"vi", "pll_p", 0, false}, + {"vi_sensor", "pll_p", 150000000, false}, + {NULL, NULL, 0, 0}, +}; + +/* GPIO */ + +//TODO: sysfs GPIO exports + +/* I2C */ + +/* Make sure that the pinmuxing enable the 'open drain' feature for pins used + for I2C */ + +/* GEN1_I2C: I2C1_SDA/SCL on MXM3 pin 209/211 (e.g. RTC on carrier board) */ +static struct i2c_board_info apalis_t30_i2c_bus1_board_info[] __initdata = { + { + /* M41T0M6 real time clock on Iris carrier board */ + I2C_BOARD_INFO("rtc-ds1307", 0x68), + .type = "m41t00", + }, +}; + +static struct tegra_i2c_platform_data apalis_t30_i2c1_platform_data = { + .adapter_nr = 0, + .arb_recovery = arb_lost_recovery, + .bus_clk_rate = {400000, 0}, + .bus_count = 1, + .scl_gpio = {I2C1_SCL, 0}, + .sda_gpio = {I2C1_SDA, 0}, + .slave_addr = 0x00FC, +}; + +/* GEN2_I2C: unused */ + +/* DDC: I2C2_SDA/SCL on MXM3 pin 205/207 (e.g. display EDID) */ +static struct tegra_i2c_platform_data apalis_t30_i2c4_platform_data = { + .adapter_nr = 3, + .arb_recovery = arb_lost_recovery, + .bus_clk_rate = {10000, 10000}, + .bus_count = 1, + .scl_gpio = {I2C2_SCL, 0}, + .sda_gpio = {I2C2_SDA, 0}, + .slave_addr = 0x00FC, +}; + +/* CAM_I2C: I2C3_SDA/SCL on MXM3 pin 201/203 (e.g. camera sensor on carrier + board) */ +static struct tegra_i2c_platform_data apalis_t30_i2c3_platform_data = { + .adapter_nr = 2, + .arb_recovery = arb_lost_recovery, + .bus_clk_rate = {400000, 0}, + .bus_count = 1, + .scl_gpio = {I2C3_SCL, 0}, + .sda_gpio = {I2C3_SDA, 0}, + .slave_addr = 0x00FC, +}; + +/* PWR_I2C: power I2C to audio codec, PMIC, temperature sensor and touch screen + controller */ + +/* STMPE811 touch screen controller */ +static struct stmpe_ts_platform_data stmpe811_ts_data = { + .adc_freq = 1, /* 3.25 MHz ADC clock speed */ + .ave_ctrl = 3, /* 8 sample average control */ + .fraction_z = 7, /* 7 length fractional part in z */ + .i_drive = 1, /* 50 mA typical 80 mA max touchscreen drivers current limit value */ + .mod_12b = 1, /* 12-bit ADC */ + .ref_sel = 0, /* internal ADC reference */ + .sample_time = 4, /* ADC converstion time: 80 clocks */ + .settling = 3, /* 1 ms panel driver settling time */ + .touch_det_delay = 5, /* 5 ms touch detect interrupt delay */ +}; + +static struct stmpe_platform_data stmpe811_data = { + .blocks = STMPE_BLOCK_TOUCHSCREEN, + .id = 1, + .irq_base = STMPE811_IRQ_BASE, + .irq_trigger = IRQF_TRIGGER_FALLING, + .ts = &stmpe811_ts_data, +}; + +static void lm95245_probe_callback(struct device *dev); + +static struct lm95245_platform_data apalis_t30_lm95245_pdata = { + .enable_os_pin = true, + .probe_callback = lm95245_probe_callback, +}; + +static struct i2c_board_info apalis_t30_i2c_bus5_board_info[] __initdata = { + { + /* SGTL5000 audio codec */ + I2C_BOARD_INFO("sgtl5000", 0x0a), + }, + { + /* STMPE811 touch screen controller */ + I2C_BOARD_INFO("stmpe", 0x41), + .flags = I2C_CLIENT_WAKE, + .irq = TEGRA_GPIO_TO_IRQ(TOUCH_PEN_INT), + .platform_data = &stmpe811_data, + .type = "stmpe811", + }, + { + /* LM95245 temperature sensor + Note: OVERT_N directly connected to PMIC PWRDN */ + I2C_BOARD_INFO("lm95245", 0x4c), + .platform_data = &apalis_t30_lm95245_pdata, + }, +}; + +static struct tegra_i2c_platform_data apalis_t30_i2c5_platform_data = { + .adapter_nr = 4, + .arb_recovery = arb_lost_recovery, + .bus_clk_rate = {400000, 0}, + .bus_count = 1, + .scl_gpio = {PWR_I2C_SCL, 0}, + .sda_gpio = {PWR_I2C_SDA, 0}, +}; + +static void __init apalis_t30_i2c_init(void) +{ + tegra_i2c_device1.dev.platform_data = &apalis_t30_i2c1_platform_data; + tegra_i2c_device3.dev.platform_data = &apalis_t30_i2c3_platform_data; + tegra_i2c_device4.dev.platform_data = &apalis_t30_i2c4_platform_data; + tegra_i2c_device5.dev.platform_data = &apalis_t30_i2c5_platform_data; + + platform_device_register(&tegra_i2c_device1); + platform_device_register(&tegra_i2c_device3); + platform_device_register(&tegra_i2c_device4); + platform_device_register(&tegra_i2c_device5); + + i2c_register_board_info(0, apalis_t30_i2c_bus1_board_info, ARRAY_SIZE(apalis_t30_i2c_bus1_board_info)); + + /* enable touch interrupt GPIO */ + gpio_request(TOUCH_PEN_INT, "TOUCH_PEN_INT"); + gpio_direction_input(TOUCH_PEN_INT); + + i2c_register_board_info(4, apalis_t30_i2c_bus5_board_info, ARRAY_SIZE(apalis_t30_i2c_bus5_board_info)); +} + +/* IrDA */ + +//TODO + +/* Keys */ + +//TODO + +/* MMC/SD */ + +static struct tegra_sdhci_platform_data apalis_t30_emmc_platform_data = { + .cd_gpio = -1, + .ddr_clk_limit = 52000000, + .is_8bit = 1, + .mmc_data = { + .built_in = 1, + }, + .power_gpio = -1, + .tap_delay = 0x0f, + .wp_gpio = -1, +}; + +static struct tegra_sdhci_platform_data apalis_t30_mmccard_platform_data = { +//GPIO tested with GPIOConfig but interrupt not working +//even 8-bit cards work if plugged during boot + .cd_gpio = MMC1_CD_N, + .ddr_clk_limit = 52000000, + .is_8bit = 1, + .power_gpio = -1, + .tap_delay = 0x0f, + .wp_gpio = -1, +}; + +static struct tegra_sdhci_platform_data apalis_t30_sdcard_platform_data = { + .cd_gpio = SD1_CD_N, + .ddr_clk_limit = 52000000, + .is_8bit = 0, + .power_gpio = -1, + .tap_delay = 0x0f, + .wp_gpio = -1, +}; + +static void __init apalis_t30_sdhci_init(void) +{ + /* register eMMC first */ + tegra_sdhci_device4.dev.platform_data = + &apalis_t30_emmc_platform_data; + platform_device_register(&tegra_sdhci_device4); + + tegra_sdhci_device3.dev.platform_data = + &apalis_t30_mmccard_platform_data; + platform_device_register(&tegra_sdhci_device3); + + tegra_sdhci_device1.dev.platform_data = + &apalis_t30_sdcard_platform_data; + platform_device_register(&tegra_sdhci_device1); +} + +/* PCIe */ + +static struct tegra_pci_platform_data apalis_t30_pci_platform_data = { + .port_status[0] = 1, + .port_status[1] = 1, + .port_status[2] = 1, + .use_dock_detect = 0, + .gpio = 0, +}; + +static void apalis_t30_pci_init(void) +{ + tegra_pci_device.dev.platform_data = &apalis_t30_pci_platform_data; + platform_device_register(&tegra_pci_device); +} + +/* PWM LEDs */ +static struct led_pwm tegra_leds_pwm[] = { + { + .name = "PWM3", + .pwm_id = 1, + .max_brightness = 255, + .pwm_period_ns = 19600, + }, + { + .name = "PWM2", + .pwm_id = 2, + .max_brightness = 255, + .pwm_period_ns = 19600, + }, + { + .name = "PWM1", + .pwm_id = 3, + .max_brightness = 255, + .pwm_period_ns = 19600, + }, +}; + +static struct led_pwm_platform_data tegra_leds_pwm_data = { + .num_leds = ARRAY_SIZE(tegra_leds_pwm), + .leds = tegra_leds_pwm, +}; + +static struct platform_device tegra_led_pwm_device = { + .name = "leds_pwm", + .id = -1, + .dev = { + .platform_data = &tegra_leds_pwm_data, + }, +}; + +/* RTC */ + +#ifdef CONFIG_RTC_DRV_TEGRA +static struct resource tegra_rtc_resources[] = { + [0] = { + .start = TEGRA_RTC_BASE, + .end = TEGRA_RTC_BASE + TEGRA_RTC_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = INT_RTC, + .end = INT_RTC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device tegra_rtc_device = { + .name = "tegra_rtc", + .id = -1, + .resource = tegra_rtc_resources, + .num_resources = ARRAY_SIZE(tegra_rtc_resources), +}; +#endif /* CONFIG_RTC_DRV_TEGRA */ + +/* SATA */ + +static struct gpio_led apalis_gpio_leds[] = { + [0] = { + .name = "SATA1_ACT_N", + .default_trigger = "ide-disk", + .gpio = SATA1_ACT_N, + .active_low = 1, + .retain_state_suspended = 0, + }, +}; + +static struct gpio_led_platform_data apalis_gpio_led_data = { + .num_leds = ARRAY_SIZE(apalis_gpio_leds), + .leds = apalis_gpio_leds, +}; + +static struct platform_device apalis_led_gpio_device = { + .name = "leds-gpio", + .dev = { + .platform_data = &apalis_gpio_led_data, + }, +}; + +#ifdef CONFIG_SATA_AHCI_TEGRA +static void apalis_t30_sata_init(void) +{ + platform_device_register(&tegra_sata_device); + platform_device_register(&apalis_led_gpio_device); +} +#else +static void apalis_t30_sata_init(void) { } +#endif + +/* SPI */ + +#if defined(CONFIG_SPI_TEGRA) && defined(CONFIG_SPI_SPIDEV) +static struct spi_board_info tegra_spi_devices[] __initdata = { + { + .bus_num = 0, /* SPI1: Apalis SPI1 */ + .chip_select = 0, + .irq = 0, + .max_speed_hz = 50000000, + .modalias = "spidev", + .mode = SPI_MODE_0, + .platform_data = NULL, + }, + { + .bus_num = 4, /* SPI5: Apalis SPI2 */ + .chip_select = 2, + .irq = 0, + .max_speed_hz = 50000000, + .modalias = "spidev", + .mode = SPI_MODE_0, + .platform_data = NULL, + }, +}; + +static void __init apalis_t30_register_spidev(void) +{ + spi_register_board_info(tegra_spi_devices, + ARRAY_SIZE(tegra_spi_devices)); +} +#else /* CONFIG_SPI_TEGRA && CONFIG_SPI_SPIDEV */ +#define apalis_t30_register_spidev() do {} while (0) +#endif /* CONFIG_SPI_TEGRA && CONFIG_SPI_SPIDEV */ + +static struct platform_device *apalis_t30_spi_devices[] __initdata = { + &tegra_spi_device1, + &tegra_spi_device2, + &tegra_spi_device4, + &tegra_spi_device5, +}; + +static struct spi_clk_parent spi_parent_clk[] = { + [0] = {.name = "pll_p"}, +#ifndef CONFIG_TEGRA_PLLM_RESTRICTED + [1] = {.name = "pll_m"}, + [2] = {.name = "clk_m"}, +#else /* !CONFIG_TEGRA_PLLM_RESTRICTED */ + [1] = {.name = "clk_m"}, +#endif /* !CONFIG_TEGRA_PLLM_RESTRICTED */ +}; + +static struct tegra_spi_platform_data apalis_t30_spi_pdata = { + .is_dma_based = true, + .max_dma_buffer = 16 * 1024, + .is_clkon_always = false, + .max_rate = 100000000, +}; + +static void __init apalis_t30_spi_init(void) +{ + int i; + struct clk *c; + + for (i = 0; i < ARRAY_SIZE(spi_parent_clk); ++i) { + c = tegra_get_clock_by_name(spi_parent_clk[i].name); + if (IS_ERR_OR_NULL(c)) { + pr_err("Not able to get the clock for %s\n", + spi_parent_clk[i].name); + continue; + } + spi_parent_clk[i].parent_clk = c; + spi_parent_clk[i].fixed_clk_rate = clk_get_rate(c); + } + apalis_t30_spi_pdata.parent_clk_list = spi_parent_clk; + apalis_t30_spi_pdata.parent_clk_count = ARRAY_SIZE(spi_parent_clk); + tegra_spi_device1.dev.platform_data = &apalis_t30_spi_pdata; + platform_add_devices(apalis_t30_spi_devices, + ARRAY_SIZE(apalis_t30_spi_devices)); +} + +/* Thermal throttling */ + +static void *apalis_t30_alert_data; +static void (*apalis_t30_alert_func)(void *); +static int apalis_t30_low_edge = 0; +static int apalis_t30_low_hysteresis = 3000; +static int apalis_t30_low_limit = 0; +static struct device *lm95245_device = NULL; +static int thermd_alert_irq_disabled = 0; +struct work_struct thermd_alert_work; +struct workqueue_struct *thermd_alert_workqueue; + +static struct balanced_throttle throttle_list[] = { +#ifdef CONFIG_TEGRA_THERMAL_THROTTLE + { + .id = BALANCED_THROTTLE_ID_TJ, + .throt_tab_size = 10, + .throt_tab = { + { 0, 1000 }, + { 640000, 1000 }, + { 640000, 1000 }, + { 640000, 1000 }, + { 640000, 1000 }, + { 640000, 1000 }, + { 760000, 1000 }, + { 760000, 1050 }, + {1000000, 1050 }, + {1000000, 1100 }, + }, + }, +#endif /* CONFIG_TEGRA_THERMAL_THROTTLE */ +#ifdef CONFIG_TEGRA_SKIN_THROTTLE + { + .id = BALANCED_THROTTLE_ID_SKIN, + .throt_tab_size = 6, + .throt_tab = { + { 640000, 1200 }, + { 640000, 1200 }, + { 760000, 1200 }, + { 760000, 1200 }, + {1000000, 1200 }, + {1000000, 1200 }, + }, + }, +#endif /* CONFIG_TEGRA_SKIN_THROTTLE */ +}; + +/* All units are in millicelsius */ +static struct tegra_thermal_data thermal_data = { + .shutdown_device_id = THERMAL_DEVICE_ID_NCT_EXT, + .temp_shutdown = 115000, + +#if defined(CONFIG_TEGRA_EDP_LIMITS) || defined(CONFIG_TEGRA_THERMAL_THROTTLE) + .throttle_edp_device_id = THERMAL_DEVICE_ID_NCT_EXT, +#endif +#ifdef CONFIG_TEGRA_EDP_LIMITS + .edp_offset = TDIODE_OFFSET, /* edp based on tdiode */ + .hysteresis_edp = 3000, +#endif +#ifdef CONFIG_TEGRA_THERMAL_THROTTLE + .temp_throttle = 85000, + .tc1 = 0, + .tc2 = 1, + .passive_delay = 2000, +#endif /* CONFIG_TEGRA_THERMAL_THROTTLE */ +#ifdef CONFIG_TEGRA_SKIN_THROTTLE + .skin_device_id = THERMAL_DEVICE_ID_SKIN, + .temp_throttle_skin = 43000, + .tc1_skin = 0, + .tc2_skin = 1, + .passive_delay_skin = 5000, + + .skin_temp_offset = 9793, + .skin_period = 1100, + .skin_devs_size = 2, + .skin_devs = { + { + THERMAL_DEVICE_ID_NCT_EXT, + { + 2, 1, 1, 1, + 1, 1, 1, 1, + 1, 1, 1, 0, + 1, 1, 0, 0, + 0, 0, -1, -7 + } + }, + { + THERMAL_DEVICE_ID_NCT_INT, + { + -11, -7, -5, -3, + -3, -2, -1, 0, + 0, 0, 1, 1, + 1, 2, 2, 3, + 4, 6, 11, 18 + } + }, + }, +#endif /* CONFIG_TEGRA_SKIN_THROTTLE */ +}; + +/* Over-temperature shutdown OS aka high limit GPIO pin interrupt handler */ +static irqreturn_t thermd_alert_irq(int irq, void *data) +{ + disable_irq_nosync(irq); + thermd_alert_irq_disabled = 1; + queue_work(thermd_alert_workqueue, &thermd_alert_work); + + return IRQ_HANDLED; +} + +/* Gets both entered by THERMD_ALERT GPIO interrupt as well as re-scheduled. */ +static void thermd_alert_work_func(struct work_struct *work) +{ + int temp = 0; + + lm95245_get_remote_temp(lm95245_device, &temp); + + /* This emulates NCT1008 low limit behaviour */ + if (!apalis_t30_low_edge && temp <= apalis_t30_low_limit) { + apalis_t30_alert_func(apalis_t30_alert_data); + apalis_t30_low_edge = 1; + } else if (apalis_t30_low_edge && temp > apalis_t30_low_limit + apalis_t30_low_hysteresis) { + apalis_t30_low_edge = 0; + } + + /* Avoid unbalanced enable for IRQ 367 */ + if (thermd_alert_irq_disabled) { + apalis_t30_alert_func(apalis_t30_alert_data); + thermd_alert_irq_disabled = 0; + enable_irq(TEGRA_GPIO_TO_IRQ(THERMD_ALERT_N)); + } + + /* Keep re-scheduling */ + msleep(2000); + queue_work(thermd_alert_workqueue, &thermd_alert_work); +} + +static int lm95245_get_temp(void *_data, long *temp) +{ + struct device *lm95245_device = _data; + int lm95245_temp = 0; + lm95245_get_remote_temp(lm95245_device, &lm95245_temp); + *temp = lm95245_temp; + return 0; +} + +static int lm95245_get_temp_low(void *_data, long *temp) +{ + *temp = 0; + return 0; +} + +/* Our temperature sensor only allows triggering an interrupt on over- + temperature shutdown aka the high limit we therefore need to setup a + workqueue to catch leaving the low limit. */ +static int lm95245_set_limits(void *_data, + long lo_limit_milli, + long hi_limit_milli) +{ + struct device *lm95245_device = _data; + apalis_t30_low_limit = lo_limit_milli; + if (lm95245_device) lm95245_set_remote_os_limit(lm95245_device, hi_limit_milli); + return 0; +} + +static int lm95245_set_alert(void *_data, + void (*alert_func)(void *), + void *alert_data) +{ + lm95245_device = _data; + apalis_t30_alert_func = alert_func; + apalis_t30_alert_data = alert_data; + return 0; +} + +static int lm95245_set_shutdown_temp(void *_data, long shutdown_temp) +{ + struct device *lm95245_device = _data; + if (lm95245_device) lm95245_set_remote_critical_limit(lm95245_device, shutdown_temp); + return 0; +} + +#ifdef CONFIG_TEGRA_SKIN_THROTTLE +/* Internal aka local board/case temp */ +static int lm95245_get_itemp(void *dev_data, long *temp) +{ + struct device *lm95245_device = dev_data; + int lm95245_temp = 0; + lm95245_get_local_temp(lm95245_device, &lm95245_temp); + *temp = lm95245_temp; + return 0; +} +#endif /* CONFIG_TEGRA_SKIN_THROTTLE */ + +static void lm95245_probe_callback(struct device *dev) +{ + struct tegra_thermal_device *lm95245_remote; + + lm95245_remote = kzalloc(sizeof(struct tegra_thermal_device), + GFP_KERNEL); + if (!lm95245_remote) { + pr_err("unable to allocate thermal device\n"); + return; + } + + lm95245_remote->name = "lm95245_remote"; + lm95245_remote->id = THERMAL_DEVICE_ID_NCT_EXT; + lm95245_remote->data = dev; + lm95245_remote->offset = TDIODE_OFFSET; + lm95245_remote->get_temp = lm95245_get_temp; + lm95245_remote->get_temp_low = lm95245_get_temp_low; + lm95245_remote->set_limits = lm95245_set_limits; + lm95245_remote->set_alert = lm95245_set_alert; + lm95245_remote->set_shutdown_temp = lm95245_set_shutdown_temp; + + tegra_thermal_device_register(lm95245_remote); + +#ifdef CONFIG_TEGRA_SKIN_THROTTLE + { + struct tegra_thermal_device *lm95245_local; + lm95245_local = kzalloc(sizeof(struct tegra_thermal_device), + GFP_KERNEL); + if (!lm95245_local) { + kfree(lm95245_local); + pr_err("unable to allocate thermal device\n"); + return; + } + + lm95245_local->name = "lm95245_local"; + lm95245_local->id = THERMAL_DEVICE_ID_NCT_INT; + lm95245_local->data = dev; + lm95245_local->get_temp = lm95245_get_itemp; + + tegra_thermal_device_register(lm95245_local); + } +#endif /* CONFIG_TEGRA_SKIN_THROTTLE */ + + if (request_irq(TEGRA_GPIO_TO_IRQ(THERMD_ALERT_N), thermd_alert_irq, + IRQF_TRIGGER_LOW, "THERMD_ALERT_N", NULL)) + pr_err("%s: unable to register THERMD_ALERT_N interrupt\n", __func__); +} + +static void apalis_t30_thermd_alert_init(void) +{ + gpio_request(THERMD_ALERT_N, "THERMD_ALERT_N"); + gpio_direction_input(THERMD_ALERT_N); + + thermd_alert_workqueue = create_singlethread_workqueue("THERMD_ALERT_N"); + + INIT_WORK(&thermd_alert_work, thermd_alert_work_func); +} + +/* UART */ + +static struct platform_device *apalis_t30_uart_devices[] __initdata = { + &tegra_uarta_device, /* Apalis UART1 */ + &tegra_uartd_device, /* Apalis UART2 */ + &tegra_uartb_device, /* Apalis UART3 */ + &tegra_uartc_device, /* Apalis UART4 */ +}; + +static struct uart_clk_parent uart_parent_clk[] = { + [0] = {.name = "clk_m"}, + [1] = {.name = "pll_p"}, +#ifndef CONFIG_TEGRA_PLLM_RESTRICTED + [2] = {.name = "pll_m"}, +#endif +}; + +static struct tegra_uart_platform_data apalis_t30_uart_pdata; + +static void __init uart_debug_init(void) +{ + int debug_port_id; + + debug_port_id = get_tegra_uart_debug_port_id(); + if (debug_port_id < 0) { + debug_port_id = 0; + } + + switch (debug_port_id) { + case 0: + /* UARTA is the debug port. */ + pr_info("Selecting UARTA as the debug console\n"); + apalis_t30_uart_devices[0] = &debug_uarta_device; + debug_uart_clk = clk_get_sys("serial8250.0", "uarta"); + debug_uart_port_base = ((struct plat_serial8250_port *)( + debug_uarta_device.dev.platform_data))->mapbase; + break; + + case 1: + /* UARTB is the debug port. */ + pr_info("Selecting UARTB as the debug console\n"); + apalis_t30_uart_devices[2] = &debug_uartb_device; + debug_uart_clk = clk_get_sys("serial8250.0", "uartb"); + debug_uart_port_base = ((struct plat_serial8250_port *)( + debug_uartb_device.dev.platform_data))->mapbase; + break; + + case 2: + /* UARTC is the debug port. */ + pr_info("Selecting UARTC as the debug console\n"); + apalis_t30_uart_devices[3] = &debug_uartc_device; + debug_uart_clk = clk_get_sys("serial8250.0", "uartc"); + debug_uart_port_base = ((struct plat_serial8250_port *)( + debug_uartb_device.dev.platform_data))->mapbase; + break; + + case 3: + /* UARTD is the debug port. */ + pr_info("Selecting UARTD as the debug console\n"); + apalis_t30_uart_devices[1] = &debug_uartd_device; + debug_uart_clk = clk_get_sys("serial8250.0", "uartd"); + debug_uart_port_base = ((struct plat_serial8250_port *)( + debug_uartd_device.dev.platform_data))->mapbase; + break; + + default: + pr_info("The debug console id %d is invalid, Assuming UARTA", debug_port_id); + apalis_t30_uart_devices[0] = &debug_uarta_device; + debug_uart_clk = clk_get_sys("serial8250.0", "uarta"); + debug_uart_port_base = ((struct plat_serial8250_port *)( + debug_uarta_device.dev.platform_data))->mapbase; + break; + } + return; +} + +static void __init apalis_t30_uart_init(void) +{ + struct clk *c; + int i; + + for (i = 0; i < ARRAY_SIZE(uart_parent_clk); ++i) { + c = tegra_get_clock_by_name(uart_parent_clk[i].name); + if (IS_ERR_OR_NULL(c)) { + pr_err("Not able to get the clock for %s\n", + uart_parent_clk[i].name); + continue; + } + uart_parent_clk[i].parent_clk = c; + uart_parent_clk[i].fixed_clk_rate = clk_get_rate(c); + } + apalis_t30_uart_pdata.parent_clk_list = uart_parent_clk; + apalis_t30_uart_pdata.parent_clk_count = ARRAY_SIZE(uart_parent_clk); + tegra_uarta_device.dev.platform_data = &apalis_t30_uart_pdata; + tegra_uartb_device.dev.platform_data = &apalis_t30_uart_pdata; + tegra_uartc_device.dev.platform_data = &apalis_t30_uart_pdata; + tegra_uartd_device.dev.platform_data = &apalis_t30_uart_pdata; + + /* Register low speed only if it is selected */ + if (!is_tegra_debug_uartport_hs()) { + uart_debug_init(); + /* Clock enable for the debug channel */ + if (!IS_ERR_OR_NULL(debug_uart_clk)) { + pr_info("The debug console clock name is %s\n", + debug_uart_clk->name); + c = tegra_get_clock_by_name("pll_p"); + if (IS_ERR_OR_NULL(c)) + pr_err("Not getting the parent clock pll_p\n"); + else + clk_set_parent(debug_uart_clk, c); + + clk_enable(debug_uart_clk); + clk_set_rate(debug_uart_clk, clk_get_rate(c)); + } else { + pr_err("Not getting the clock %s for debug console\n", + debug_uart_clk->name); + } + } + + platform_add_devices(apalis_t30_uart_devices, + ARRAY_SIZE(apalis_t30_uart_devices)); +} + +/* USB */ + +//TODO: overcurrent + +static struct tegra_usb_platform_data tegra_udc_pdata = { + .has_hostpc = true, + .op_mode = TEGRA_USB_OPMODE_DEVICE, + .phy_intf = TEGRA_USB_PHY_INTF_UTMI, + .port_otg = true, + .u_cfg.utmi = { + .elastic_limit = 16, + .hssync_start_delay = 0, + .idle_wait_delay = 17, + .term_range_adj = 6, + .xcvr_lsfslew = 2, + .xcvr_lsrslew = 2, + .xcvr_setup = 8, + .xcvr_setup_offset = 0, + .xcvr_use_fuses = 1, + }, + .u_data.dev = { + .charging_supported = false, + .remote_wakeup_supported = false, + .vbus_gpio = -1, + .vbus_pmu_irq = 0, + }, +}; + +static struct tegra_usb_platform_data tegra_ehci1_utmi_pdata = { + .has_hostpc = true, + .op_mode = TEGRA_USB_OPMODE_HOST, + .phy_intf = TEGRA_USB_PHY_INTF_UTMI, + .port_otg = true, + .u_cfg.utmi = { + .elastic_limit = 16, + .hssync_start_delay = 0, + .idle_wait_delay = 17, + .term_range_adj = 6, + .xcvr_lsfslew = 2, + .xcvr_lsrslew = 2, + .xcvr_setup = 15, + .xcvr_setup_offset = 0, + .xcvr_use_fuses = 1, + }, + .u_data.host = { + .hot_plug = true, + .power_off_on_suspend = false, + .remote_wakeup_supported = true, + .vbus_gpio = USBO1_EN, + .vbus_gpio_inverted = 0, + .vbus_reg = NULL, + }, +}; + +static struct tegra_usb_platform_data tegra_ehci2_utmi_pdata = { + .has_hostpc = true, + .op_mode = TEGRA_USB_OPMODE_HOST, + .phy_intf = TEGRA_USB_PHY_INTF_UTMI, + .port_otg = false, + .u_cfg.utmi = { + .elastic_limit = 16, + .hssync_start_delay = 0, + .idle_wait_delay = 17, + .term_range_adj = 6, + .xcvr_lsfslew = 2, + .xcvr_lsrslew = 2, + .xcvr_setup = 15, + .xcvr_setup_offset = 0, + .xcvr_use_fuses = 1, + }, + .u_data.host = { + .hot_plug = true, + .power_off_on_suspend = false, + .remote_wakeup_supported = true, + .vbus_gpio = USBH_EN, + .vbus_gpio_inverted = 0, + .vbus_reg = NULL, + }, +}; + +static struct tegra_usb_platform_data tegra_ehci3_utmi_pdata = { + .has_hostpc = true, + .op_mode = TEGRA_USB_OPMODE_HOST, + .phy_intf = TEGRA_USB_PHY_INTF_UTMI, + .port_otg = false, + .u_cfg.utmi = { + .elastic_limit = 16, + .hssync_start_delay = 0, + .idle_wait_delay = 17, + .term_range_adj = 6, + .xcvr_lsfslew = 2, + .xcvr_lsrslew = 2, + .xcvr_setup = 8, + .xcvr_setup_offset = 0, + .xcvr_use_fuses = 1, + }, + .u_data.host = { + .hot_plug = true, + .power_off_on_suspend = false, + .remote_wakeup_supported = true, + /* Uses same USBH_EN as EHCI2 */ + .vbus_gpio = -1, + .vbus_gpio_inverted = 0, + .vbus_reg = NULL, + }, +}; + +static struct tegra_usb_otg_data tegra_otg_pdata = { + .ehci_device = &tegra_ehci1_device, + .ehci_pdata = &tegra_ehci1_utmi_pdata, +}; + +static void apalis_t30_usb_init(void) +{ + /* OTG should be the first to be registered + EHCI instance 0: USB1_DP/N -> USBO1_DP/N */ + tegra_otg_device.dev.platform_data = &tegra_otg_pdata; + platform_device_register(&tegra_otg_device); + + /* setup the udc platform data */ + tegra_udc_device.dev.platform_data = &tegra_udc_pdata; + platform_device_register(&tegra_udc_device); + + /* EHCI instance 1: USB2_DP/N -> USBH2_DP/N */ + tegra_ehci2_device.dev.platform_data = &tegra_ehci2_utmi_pdata; + platform_device_register(&tegra_ehci2_device); + + /* EHCI instance 2: USB3_DP/N -> USBH3_DP/N */ + tegra_ehci3_device.dev.platform_data = &tegra_ehci3_utmi_pdata; + platform_device_register(&tegra_ehci3_device); +} + +/* W1, aka OWR, aka OneWire */ + +#ifdef CONFIG_W1_MASTER_TEGRA +struct tegra_w1_timings apalis_t30_w1_timings = { + .tsu = 1, + .trelease = 0xf, + .trdv = 0xf, + .tlow0 = 0x3c, + .tlow1 = 1, + .tslot = 0x77, + + .tpdl = 0x78, + .tpdh = 0x1e, + .trstl = 0x1df, + .trsth = 0x1df, + .rdsclk = 0x7, + .psclk = 0x50, +}; + +struct tegra_w1_platform_data apalis_t30_w1_platform_data = { + .clk_id = "tegra_w1", + .timings = &apalis_t30_w1_timings, +}; +#endif /* CONFIG_W1_MASTER_TEGRA */ + +static struct platform_device *apalis_t30_devices[] __initdata = { + &tegra_pmu_device, +#if defined(CONFIG_RTC_DRV_TEGRA) + &tegra_rtc_device, +#endif +#if defined(CONFIG_TEGRA_IOVMM_SMMU) || defined(CONFIG_TEGRA_IOMMU_SMMU) + &tegra_smmu_device, +#endif + &tegra_wdt0_device, + &tegra_wdt1_device, + &tegra_wdt2_device, +#if defined(CONFIG_TEGRA_AVP) + &tegra_avp_device, +#endif +#ifdef CONFIG_TEGRA_CAMERA + &tegra_camera, +#endif +#if defined(CONFIG_CRYPTO_DEV_TEGRA_SE) + &tegra_se_device, +#endif +#if defined(CONFIG_CRYPTO_DEV_TEGRA_AES) + &tegra_aes_device, +#endif + + &tegra_ahub_device, + &tegra_dam_device0, + &tegra_dam_device1, + &tegra_dam_device2, + &tegra_i2s_device2, + &tegra_spdif_device, + &spdif_dit_device, + &tegra_pcm_device, + &apalis_t30_audio_sgtl5000_device, + + &tegra_cec_device, +#if defined(CONFIG_CRYPTO_DEV_TEGRA_AES) + &tegra_aes_device, +#endif +#ifdef CONFIG_KEYBOARD_GPIO +// &apalis_t30_keys_device, +#endif + &tegra_led_pwm_device, + &tegra_pwfm1_device, + &tegra_pwfm2_device, + &tegra_pwfm3_device, +#ifdef CONFIG_W1_MASTER_TEGRA + &tegra_w1_device, +#endif +}; + +static void __init apalis_t30_init(void) +{ + tegra_thermal_init(&thermal_data, + throttle_list, + ARRAY_SIZE(throttle_list)); + tegra_clk_init_from_table(apalis_t30_clk_init_table); + apalis_t30_pinmux_init(); + apalis_t30_thermd_alert_init(); + apalis_t30_i2c_init(); + apalis_t30_spi_init(); + apalis_t30_usb_init(); +#ifdef CONFIG_TEGRA_EDP_LIMITS + apalis_t30_edp_init(); +#endif + apalis_t30_uart_init(); +#ifdef CONFIG_W1_MASTER_TEGRA + tegra_w1_device.dev.platform_data = &apalis_t30_w1_platform_data; +#endif + platform_add_devices(apalis_t30_devices, ARRAY_SIZE(apalis_t30_devices)); + tegra_ram_console_debug_init(); + tegra_io_dpd_init(); + apalis_t30_sdhci_init(); + apalis_t30_regulator_init(); + apalis_t30_suspend_init(); + apalis_t30_panel_init(); +// apalis_t30_sensors_init(); + apalis_t30_sata_init(); + apalis_t30_emc_init(); + apalis_t30_register_spidev(); + + tegra_release_bootloader_fb(); + apalis_t30_pci_init(); +#ifdef CONFIG_TEGRA_WDT_RECOVERY + tegra_wdt_recovery_init(); +#endif + tegra_serial_debug_init(TEGRA_UARTD_BASE, INT_WDT_CPU, NULL, -1, -1); + apalis_t20_mcp2515_can_init(); +} + +static void __init apalis_t30_reserve(void) +{ +#if defined(CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM) + /* Support 1920X1080 32bpp,double buffered on HDMI*/ + tegra_reserve(0, SZ_8M + SZ_1M, SZ_16M); +#else + tegra_reserve(SZ_128M, SZ_8M, SZ_8M); +#endif + tegra_ram_console_debug_reserve(SZ_1M); +} + +static const char *apalis_t30_dt_board_compat[] = { + "toradex,apalis_t30", + NULL +}; + +MACHINE_START(APALIS_T30, "Toradex Apalis T30") + .boot_params = 0x80000100, + .dt_compat = apalis_t30_dt_board_compat, + .init_early = tegra_init_early, + .init_irq = tegra_init_irq, + .init_machine = apalis_t30_init, + .map_io = tegra_map_common_io, + .reserve = apalis_t30_reserve, + .timer = &tegra_timer, +MACHINE_END |