summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/board-apalis_t30.c
diff options
context:
space:
mode:
authorMarcel Ziswiler <marcel.ziswiler@toradex.com>2013-01-24 15:27:07 +0100
committerMarcel Ziswiler <marcel.ziswiler@toradex.com>2013-01-24 15:27:07 +0100
commitb37acc053dd43475e67e2733a352c4f77e4f65c2 (patch)
tree6457cb6903033df1ccc7d025637b134cb6b14d5e /arch/arm/mach-tegra/board-apalis_t30.c
parentd49a5dea0456773992edf0fa3b2d4e46e0f1ef6c (diff)
tegra: apalis_t30: initial Toradex Apalis T30 L4T support
Tested on early prototype Apalis T30 V1.0a module. Known issues: - ADC not integrated yet. - HDA not integrated yet. - CEC not integrated yet. - IrDA not integrated yet. - Keys not integrated yet therefore no way to wake from suspend. - 8-bit MMC1 slot card detection interrupt not working despite detection GPIO successfully being tested with GPIOConfig. Note: even 8-bit cards work fine if already plugged-in during boot. - PCIe limited to internal Gigabit Ethernet chip for now due to our proprietary way of resetting other ports which requires further integration into NVIDIA's driver.
Diffstat (limited to 'arch/arm/mach-tegra/board-apalis_t30.c')
-rw-r--r--arch/arm/mach-tegra/board-apalis_t30.c1196
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