From 377cf9c7a6d8c3d50836dbc26580384f7660a8fc Mon Sep 17 00:00:00 2001 From: Dongjin Kim Date: Mon, 30 Jul 2012 10:15:56 +0900 Subject: [PATCH 01/50] ODROID-X: hkdk4412: Add new hardware based on Exynos4412 The HKDK4412 is the CPU module developed by Hardkernel which is based on Exynos 4412. mach_hkdk4412.c is to run on ODROID-X hardware and Machine ID (4289) is registered. Change-Id: I3af02808c03b9acea916c310390a4904ad92d761 Signed-off-by: Dongjin Kim --- arch/arm/mach-exynos/Kconfig | 23 + arch/arm/mach-exynos/Makefile | 1 + arch/arm/mach-exynos/mach-hkdk4412.c | 979 +++++++++++++++++++++++++++ arch/arm/tools/mach-types | 1 + 4 files changed, 1004 insertions(+) create mode 100644 arch/arm/mach-exynos/mach-hkdk4412.c diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index b5b4c8c9db11ad..ae9077373d5769 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -54,6 +54,9 @@ config SOC_EXYNOS4412 default y depends on ARCH_EXYNOS4 select SAMSUNG_DMADEV + select S5P_PM if PM + select S5P_SLEEP if PM + select PM_GENERIC_DOMAINS help Enable EXYNOS4412 SoC support @@ -394,6 +397,26 @@ config MACH_SMDK4412 select MACH_SMDK4212 help Machine support for Samsung SMDK4412 + +config MACH_HKDK4412 + bool "HKDK4412" + select SOC_EXYNOS4412 + select MACH_SMDK4212 + select S3C_DEV_HSMMC + select S3C_DEV_HSMMC1 + select S3C_DEV_USB_HSOTG + select S5P_DEV_CSIS0 + select S5P_DEV_G2D + select S5P_DEV_JPEG + select S5P_DEV_USB_EHCI + select S5P_DEV_I2C_HDMIPHY + select S5P_DEV_TV + select S5P_GPIO_INT + select EXYNOS4_DEV_USB_OHCI + select SAMSUNG_DEV_ADC + help + Machine support for Odroid-X based on Samsung EXYNOS4412 + endif comment "Flattened Device Tree based board for EXYNOS SoCs" diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index 9b58024f7d4391..dccc3812eba076 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_MACH_ORIGEN) += mach-origen.o obj-$(CONFIG_MACH_SMDK4212) += mach-smdk4x12.o obj-$(CONFIG_MACH_SMDK4412) += mach-smdk4x12.o +obj-$(CONFIG_MACH_HKDK4412) += mach-hkdk4412.o obj-$(CONFIG_MACH_EXYNOS4_DT) += mach-exynos4-dt.o obj-$(CONFIG_MACH_EXYNOS5_DT) += mach-exynos5-dt.o diff --git a/arch/arm/mach-exynos/mach-hkdk4412.c b/arch/arm/mach-exynos/mach-hkdk4412.c new file mode 100644 index 00000000000000..3b090ae97634f8 --- /dev/null +++ b/arch/arm/mach-exynos/mach-hkdk4412.c @@ -0,0 +1,979 @@ +/* + * linux/arch/arm/mach-exynos4/mach-hkdk4412.c + * + * Copyright (c) 2012 AgreeYa Mobility Co., Ltd. + * http://www.agreeyamobility.net + * + * 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. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "common.h" + +/* Following are default values for UCON, ULCON and UFCON UART registers */ +#define HKDK4412_UCON_DEFAULT (S3C2410_UCON_TXILEVEL | \ + S3C2410_UCON_RXILEVEL | \ + S3C2410_UCON_TXIRQMODE | \ + S3C2410_UCON_RXIRQMODE | \ + S3C2410_UCON_RXFIFO_TOI | \ + S3C2443_UCON_RXERR_IRQEN) + +#define HKDK4412_ULCON_DEFAULT S3C2410_LCON_CS8 + +#define HKDK4412_UFCON_DEFAULT (S3C2410_UFCON_FIFOMODE | \ + S5PV210_UFCON_TXTRIG4 | \ + S5PV210_UFCON_RXTRIG4) + +static struct s3c2410_uartcfg hkdk4412_uartcfgs[] __initdata = { + [0] = { + .hwport = 0, + .flags = 0, + .ucon = HKDK4412_UCON_DEFAULT, + .ulcon = HKDK4412_ULCON_DEFAULT, + .ufcon = HKDK4412_UFCON_DEFAULT, + }, + [1] = { + .hwport = 1, + .flags = 0, + .ucon = HKDK4412_UCON_DEFAULT, + .ulcon = HKDK4412_ULCON_DEFAULT, + .ufcon = HKDK4412_UFCON_DEFAULT, + }, + [2] = { + .hwport = 2, + .flags = 0, + .ucon = HKDK4412_UCON_DEFAULT, + .ulcon = HKDK4412_ULCON_DEFAULT, + .ufcon = HKDK4412_UFCON_DEFAULT, + }, + [3] = { + .hwport = 3, + .flags = 0, + .ucon = HKDK4412_UCON_DEFAULT, + .ulcon = HKDK4412_ULCON_DEFAULT, + .ufcon = HKDK4412_UFCON_DEFAULT, + }, +}; + +static struct s3c_sdhci_platdata hkdk4412_hsmmc2_pdata __initdata = { + .cd_type = S3C_SDHCI_CD_INTERNAL, +#ifdef CONFIG_EXYNOS4_SDHCI_CH2_8BIT + .max_width = 8, + .host_caps = MMC_CAP_8_BIT_DATA, +#endif +}; + +static struct s3c_sdhci_platdata hkdk4412_hsmmc3_pdata __initdata = { + .cd_type = S3C_SDHCI_CD_INTERNAL, +}; + +static struct regulator_consumer_supply __initdata max77686_buck1_consumer[] = { + REGULATOR_SUPPLY("vdd_mif", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_buck2_consumer[] = { + REGULATOR_SUPPLY("vdd_arm", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_buck3_consumer[] = { + REGULATOR_SUPPLY("vdd_int", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_buck4_consumer[] = { + REGULATOR_SUPPLY("vdd_g3d", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo1_consumer[] = { + REGULATOR_SUPPLY("vdd_alive", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo3_consumer[] = { + REGULATOR_SUPPLY("vddq_aud", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo4_consumer[] = { + REGULATOR_SUPPLY("vddq_mmc2", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo5_consumer[] = { + REGULATOR_SUPPLY("vddq_mmc1", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo8_consumer[] = { + REGULATOR_SUPPLY("vdd10_mipi", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo10_consumer[] = { + REGULATOR_SUPPLY("vdd18_mipi", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo11_consumer[] = { + REGULATOR_SUPPLY("vdd_ldo11", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo13_consumer[] = { + REGULATOR_SUPPLY("vdd18_mipihsi", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo14_consumer[] = { + REGULATOR_SUPPLY("vdd_ldo14", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo17_consumer[] = { + REGULATOR_SUPPLY("vddq_cam", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo18_consumer[] = { + REGULATOR_SUPPLY("vddq_isp", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo19_consumer[] = { + REGULATOR_SUPPLY("vt_cam", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo23_consumer[] = { + REGULATOR_SUPPLY("vdd_touch", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo24_consumer[] = { + REGULATOR_SUPPLY("vdd_touchled", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo25_consumer[] = { + REGULATOR_SUPPLY("vddq_lcd", NULL), +}; + +static struct regulator_consumer_supply __initdata max77686_ldo26_consumer[] = { + REGULATOR_SUPPLY("vdd_motor", NULL), +}; + +static struct regulator_init_data __initdata max77686_buck1_data = { + .constraints = { + .name = "vdd_mif range", + .min_uV = 800000, + .max_uV = 1050000, + .always_on = 1, + .boot_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_STATUS, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_buck1_consumer), + .consumer_supplies = max77686_buck1_consumer, +}; + +static struct regulator_init_data __initdata max77686_buck2_data = { + .constraints = { + .name = "vdd_arm range", + .min_uV = 800000, + .max_uV = 1350000, + .always_on = 1, + .boot_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_buck2_consumer), + .consumer_supplies = max77686_buck2_consumer, +}; + +static struct regulator_init_data __initdata max77686_buck3_data = { + .constraints = { + .name = "vdd_int range", + .min_uV = 800000, + .max_uV = 1150000, + .always_on = 1, + .boot_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_buck3_consumer), + .consumer_supplies = max77686_buck3_consumer, +}; + +static struct regulator_init_data __initdata max77686_buck4_data = { + .constraints = { + .name = "vdd_g3d range", + .min_uV = 850000, + .max_uV = 1200000, + .boot_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE | + REGULATOR_CHANGE_STATUS, + .state_mem = { + .disabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_buck4_consumer), + .consumer_supplies = max77686_buck4_consumer, +}; + +static struct regulator_init_data __initdata max77686_buck5_data = { + .constraints = { + .name = "BUCK5 VDDQ_CKEM1_2", + .min_uV = 1200000, + .max_uV = 1200000, + .always_on = 1, + .apply_uV = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1200000, + .mode = REGULATOR_MODE_NORMAL, + .enabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_buck6_data = { + .constraints = { + .name = "BUCK6 1V35", + .min_uV = 1350000, + .max_uV = 1350000, + .always_on = 1, + .apply_uV = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1350000, + .mode = REGULATOR_MODE_NORMAL, + .enabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_buck7_data = { + .constraints = { + .name = "BUCK7 2V0", + .min_uV = 2000000, + .max_uV = 2000000, + .always_on = 1, + .apply_uV = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 2000000, + .mode = REGULATOR_MODE_NORMAL, + .enabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_buck8_data = { + .constraints = { + .name = "BUCK8 3V3", + .min_uV = 3300000, + .max_uV = 3300000, + .always_on = 1, + .apply_uV = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 3300000, + .mode = REGULATOR_MODE_NORMAL, + .enabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_buck9_data = { + .constraints = { + .name = "BUCK9 1V2", + .min_uV = 1200000, + .max_uV = 1200000, + .always_on = 1, + .apply_uV = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1200000, + .mode = REGULATOR_MODE_NORMAL, + .enabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_ldo1_data = { + .constraints = { + .name = "LDO1 VDD_ALIVE", + .min_uV = 1000000, + .max_uV = 1000000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1000000, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo1_consumer), + .consumer_supplies = max77686_ldo1_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo2_data = { + .constraints = { + .name = "LDO2 VDDQ_M1_1V8", + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1800000, + .enabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_ldo3_data = { + .constraints = { + .name = "LDO3 VDDQ_AUD_1V8", + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1800000, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo3_consumer), + .consumer_supplies = max77686_ldo3_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo4_data = { + .constraints = { + .name = "LDO4 VDDQ_MMC2_2V8", + .min_uV = 2800000, + .max_uV = 2800000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 2800000, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo4_consumer), + .consumer_supplies = max77686_ldo4_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo5_data = { + .constraints = { + .name = "LDO5 VDDQ_MMC1_1V8", + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1800000, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo5_consumer), + .consumer_supplies = max77686_ldo5_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo6_data = { + .constraints = { + .name = "LDO6 VDD10_MPLL_1V0", + .min_uV = 1000000, + .max_uV = 1000000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1000000, + .enabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_ldo7_data = { + .constraints = { + .name = "LDO7 VDD10_EPLL_1V0", + .min_uV = 1000000, + .max_uV = 1000000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1000000, + .enabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_ldo8_data = { + .constraints = { + .name = "LDO8 VDD10_MIPI_1V0", + .min_uV = 1000000, + .max_uV = 1000000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1000000, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo8_consumer), + .consumer_supplies = max77686_ldo8_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo9_data = { + .constraints = { + .name = "LDO9 VT_CORE_1V0", + .min_uV = 1000000, + .max_uV = 1000000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1000000, + .enabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_ldo10_data = { + .constraints = { + .name = "LDO10 VDD18_MIPI_1V8", + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1800000, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo10_consumer), + .consumer_supplies = max77686_ldo10_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo11_data = { + .constraints = { + .name = "vdd_ldo11 range", + .min_uV = 1900000, + .max_uV = 1900000, + .apply_uV = 1, + .always_on = 1, + .state_mem = { + .uV = 1900000, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo11_consumer), + .consumer_supplies = max77686_ldo11_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo12_data = { + .constraints = { + .name = "LDO12 VDD33_UOTG_3V3", + .min_uV = 3300000, + .max_uV = 3300000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 3300000, + .enabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_ldo13_data = { + .constraints = { + .name = "LDO13 VDD18_MIPIHSI_1V8", + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1800000, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo13_consumer), + .consumer_supplies = max77686_ldo13_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo14_data = { + .constraints = { + .name = "vdd_ldo14 range", + .min_uV = 1900000, + .max_uV = 1900000, + .apply_uV = 1, + .always_on = 1, + .state_mem = { + .uV = 1900000, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo14_consumer), + .consumer_supplies = max77686_ldo14_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo15_data = { + .constraints = { + .name = "LDO15 VDD10_OTG", + .min_uV = 1000000, + .max_uV = 1000000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1000000, + .enabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_ldo16_data = { + .constraints = { + .name = "LDO16 VDD18_HSIC", + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1800000, + .enabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_ldo17_data = { + .constraints = { + .name = "LDO17 VDDQ_CAM_1V8", + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1800000, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo17_consumer), + .consumer_supplies = max77686_ldo17_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo18_data = { + .constraints = { + .name = "LDO18 VDDQ_ISP_1V8", + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1800000, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo18_consumer), + .consumer_supplies = max77686_ldo18_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo19_data = { + .constraints = { + .name = "LDO19 VT_CAM_1V8", + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = 0, + .always_on = 0, + .boot_on = 0, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1800000, + .enabled = 0, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo19_consumer), + .consumer_supplies = max77686_ldo19_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo20_data = { + .constraints = { + .name = "LDO20 EMMC_IO_1V8", + .min_uV = 1900000, + .max_uV = 1900000, + .always_on = 1, + .boot_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 1900000, + .enabled = 0, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_ldo21_data = { + .constraints = { + .name = "LDO21 TFLASH_2V8", + .min_uV = 3300000, + .max_uV = 3300000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 2800000, + .enabled = 1, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_ldo22_data = { + .constraints = { + .name = "LDO22", + .min_uV = 3300000, + .max_uV = 3300000, + .apply_uV = 1, + .always_on = 0, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 3300000, + .enabled = 0, + }, + }, +}; + +static struct regulator_init_data __initdata max77686_ldo23_data = { + .constraints = { + .name = "LDO23 VDD_TOUCH_2V8", + .min_uV = 2800000, + .max_uV = 2800000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 2800000, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo23_consumer), + .consumer_supplies = max77686_ldo23_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo24_data = { + .constraints = { + .name = "LDO24 VDD_TOUCHLED_3V3", + .min_uV = 3300000, + .max_uV = 3300000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 3300000, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo24_consumer), + .consumer_supplies = max77686_ldo24_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo25_data = { + .constraints = { + .name = "LDO25 VDDQ_LCD_1V8", + .min_uV = 1800000, + .max_uV = 1800000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .disabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo25_consumer), + .consumer_supplies = max77686_ldo25_consumer, +}; + +static struct regulator_init_data __initdata max77686_ldo26_data = { + .constraints = { + .name = "LDO26 VDD_MOTOR_3V0", + .min_uV = 3000000, + .max_uV = 3000000, + .apply_uV = 1, + .always_on = 1, + .valid_ops_mask = REGULATOR_CHANGE_STATUS, + .state_mem = { + .uV = 3000000, + .enabled = 1, + }, + }, + .num_consumer_supplies = ARRAY_SIZE(max77686_ldo26_consumer), + .consumer_supplies = max77686_ldo26_consumer, +}; + +struct max77686_opmode_data max77686_opmode_data[MAX77686_REG_MAX] = { + [MAX77686_LDO11] = {MAX77686_LDO11, MAX77686_OPMODE_STANDBY}, + [MAX77686_LDO14] = {MAX77686_LDO14, MAX77686_OPMODE_STANDBY}, + [MAX77686_BUCK1] = {MAX77686_BUCK1, MAX77686_OPMODE_STANDBY}, + [MAX77686_BUCK2] = {MAX77686_BUCK2, MAX77686_OPMODE_STANDBY}, + [MAX77686_BUCK3] = {MAX77686_BUCK3, MAX77686_OPMODE_STANDBY}, + [MAX77686_BUCK4] = {MAX77686_BUCK4, MAX77686_OPMODE_STANDBY}, +}; + +static struct max77686_regulator_data max77686_regulators[] = { + { MAX77686_LDO1, &max77686_ldo1_data }, + { MAX77686_LDO2, &max77686_ldo2_data }, + { MAX77686_LDO3, &max77686_ldo3_data }, + { MAX77686_LDO4, &max77686_ldo4_data }, + { MAX77686_LDO5, &max77686_ldo5_data }, + { MAX77686_LDO6, &max77686_ldo6_data }, + { MAX77686_LDO7, &max77686_ldo7_data }, + { MAX77686_LDO8, &max77686_ldo8_data }, + { MAX77686_LDO9, &max77686_ldo9_data }, + { MAX77686_LDO10, &max77686_ldo10_data }, + { MAX77686_LDO11, &max77686_ldo11_data }, + { MAX77686_LDO12, &max77686_ldo12_data }, + { MAX77686_LDO13, &max77686_ldo13_data }, + { MAX77686_LDO14, &max77686_ldo14_data }, + { MAX77686_LDO15, &max77686_ldo15_data }, + { MAX77686_LDO16, &max77686_ldo16_data }, + { MAX77686_LDO17, &max77686_ldo17_data }, + { MAX77686_LDO18, &max77686_ldo18_data }, + { MAX77686_LDO19, &max77686_ldo19_data }, + { MAX77686_LDO20, &max77686_ldo20_data }, + { MAX77686_LDO21, &max77686_ldo21_data }, + { MAX77686_LDO22, &max77686_ldo22_data }, + { MAX77686_LDO23, &max77686_ldo23_data }, + { MAX77686_LDO24, &max77686_ldo24_data }, + { MAX77686_LDO25, &max77686_ldo25_data }, + { MAX77686_LDO26, &max77686_ldo26_data }, + + { MAX77686_BUCK1, &max77686_buck1_data }, + { MAX77686_BUCK2, &max77686_buck2_data }, + { MAX77686_BUCK3, &max77686_buck3_data }, + { MAX77686_BUCK4, &max77686_buck4_data }, + { MAX77686_BUCK5, &max77686_buck5_data }, + { MAX77686_BUCK6, &max77686_buck6_data }, + { MAX77686_BUCK7, &max77686_buck7_data }, + { MAX77686_BUCK8, &max77686_buck8_data }, + { MAX77686_BUCK9, &max77686_buck9_data }, +}; + +static struct max77686_platform_data hkdk4412_max77686_info = { + .num_regulators = ARRAY_SIZE(max77686_regulators), + .regulators = max77686_regulators, + .irq_gpio = 0, + .wakeup = 0, + + .opmode_data = max77686_opmode_data, + + .buck234_gpio_dvs[0] = EXYNOS4_GPX2(3), + .buck234_gpio_dvs[1] = EXYNOS4_GPX2(4), + .buck234_gpio_dvs[2] = EXYNOS4_GPX2(5), + + .buck2_voltage[0] = 1300000, /* 1.3V */ + .buck2_voltage[1] = 1000000, /* 1.0V */ + .buck2_voltage[2] = 950000, /* 0.95V */ + .buck2_voltage[3] = 900000, /* 0.9V */ + .buck2_voltage[4] = 1000000, /* 1.0V */ + .buck2_voltage[5] = 1000000, /* 1.0V */ + .buck2_voltage[6] = 950000, /* 0.95V */ + .buck2_voltage[7] = 900000, /* 0.9V */ + + .buck3_voltage[0] = 1037500, /* 1.0375V */ + .buck3_voltage[1] = 1000000, /* 1.0V */ + .buck3_voltage[2] = 950000, /* 0.95V */ + .buck3_voltage[3] = 900000, /* 0.9V */ + .buck3_voltage[4] = 1000000, /* 1.0V */ + .buck3_voltage[5] = 1000000, /* 1.0V */ + .buck3_voltage[6] = 950000, /* 0.95V */ + .buck3_voltage[7] = 900000, /* 0.9V */ + + .buck4_voltage[0] = 1100000, /* 1.1V */ + .buck4_voltage[1] = 1000000, /* 1.0V */ + .buck4_voltage[2] = 950000, /* 0.95V */ + .buck4_voltage[3] = 900000, /* 0.9V */ + .buck4_voltage[4] = 1000000, /* 1.0V */ + .buck4_voltage[5] = 1000000, /* 1.0V */ + .buck4_voltage[6] = 950000, /* 0.95V */ + .buck4_voltage[7] = 900000, /* 0.9V */ +}; + +static struct i2c_board_info hkdk4412_i2c_devs0[] __initdata = { + { + I2C_BOARD_INFO("max77686", (0x12 >> 1)), + .platform_data = &hkdk4412_max77686_info, + } +}; + +static struct i2c_board_info hkdk4412_i2c_devs1[] __initdata = { +}; + +static struct i2c_board_info hkdk4412_i2c_devs3[] __initdata = { + /* nothing here yet */ +}; + +static struct i2c_board_info hkdk4412_i2c_devs7[] __initdata = { + /* nothing here yet */ +}; + +static struct gpio_led hkdk4412_gpio_leds[] = { + { + .name = "led1", /* D5 on ODROID-X */ + .default_trigger = "oneshot", + .gpio = EXYNOS4_GPC1(0), + .active_low = 1, + }, + { + .name = "led2", /* D6 on ODROID-X */ + .default_trigger = "heartbeat", + .gpio = EXYNOS4_GPC1(2), + .active_low = 1, + }, +}; + +static struct gpio_led_platform_data hkdk4412_gpio_led_info = { + .leds = hkdk4412_gpio_leds, + .num_leds = ARRAY_SIZE(hkdk4412_gpio_leds), +}; + +static struct platform_device hkdk4412_leds_gpio = { + .name = "leds-gpio", + .id = -1, + .dev = { + .platform_data = &hkdk4412_gpio_led_info, + }, +}; + +/* USB EHCI */ +static struct s5p_ehci_platdata hkdk4412_ehci_pdata; + +static void __init hkdk4412_ehci_init(void) +{ + struct s5p_ehci_platdata *pdata = &hkdk4412_ehci_pdata; + + s5p_ehci_set_platdata(pdata); +} + +/* USB OHCI */ +static struct exynos4_ohci_platdata hkdk4412_ohci_pdata; + +static void __init hkdk4412_ohci_init(void) +{ + struct exynos4_ohci_platdata *pdata = &hkdk4412_ohci_pdata; + + exynos4_ohci_set_platdata(pdata); +} + +/* USB OTG */ +static struct s3c_hsotg_plat hkdk4412_hsotg_pdata; + +static struct platform_device *hkdk4412_devices[] __initdata = { + &s3c_device_hsmmc2, + &s3c_device_hsmmc3, + &s3c_device_i2c0, + &s3c_device_i2c1, + &s3c_device_i2c3, + &s3c_device_i2c7, + &s3c_device_rtc, + &s3c_device_usb_hsotg, + &s3c_device_wdt, + &s5p_device_ehci, + &s5p_device_fimc0, + &s5p_device_fimc1, + &s5p_device_fimc2, + &s5p_device_fimc3, + &s5p_device_fimc_md, + &s5p_device_mfc, + &s5p_device_mfc_l, + &s5p_device_mfc_r, + &exynos4_device_ohci, + &hkdk4412_leds_gpio, +}; + +static void __init hkdk4412_map_io(void) +{ + clk_xusbxti.rate = 24000000; + + exynos_init_io(NULL, 0); + s3c24xx_init_clocks(clk_xusbxti.rate); + s3c24xx_init_uarts(hkdk4412_uartcfgs, ARRAY_SIZE(hkdk4412_uartcfgs)); +} + +static void __init hkdk4412_reserve(void) +{ + s5p_mfc_reserve_mem(0x43000000, 8 << 20, 0x51000000, 8 << 20); +} + +static void __init hkdk4412_gpio_init(void) +{ + /* Peripheral power enable (P3V3) */ + gpio_request_one(EXYNOS4_GPA1(1), GPIOF_OUT_INIT_HIGH, "p3v3_en"); +} + +static void __init hkdk4412_machine_init(void) +{ + hkdk4412_gpio_init(); + + s3c_i2c0_set_platdata(NULL); + i2c_register_board_info(0, hkdk4412_i2c_devs0, + ARRAY_SIZE(hkdk4412_i2c_devs0)); + + s3c_i2c1_set_platdata(NULL); + i2c_register_board_info(1, hkdk4412_i2c_devs1, + ARRAY_SIZE(hkdk4412_i2c_devs1)); + + s3c_i2c3_set_platdata(NULL); + i2c_register_board_info(3, hkdk4412_i2c_devs3, + ARRAY_SIZE(hkdk4412_i2c_devs3)); + + s3c_i2c7_set_platdata(NULL); + i2c_register_board_info(7, hkdk4412_i2c_devs7, + ARRAY_SIZE(hkdk4412_i2c_devs7)); + + s3c_sdhci2_set_platdata(&hkdk4412_hsmmc2_pdata); + s3c_sdhci3_set_platdata(&hkdk4412_hsmmc3_pdata); + + hkdk4412_ehci_init(); + hkdk4412_ohci_init(); + s3c_hsotg_set_platdata(&hkdk4412_hsotg_pdata); + + platform_add_devices(hkdk4412_devices, ARRAY_SIZE(hkdk4412_devices)); +} + +MACHINE_START(ODROIDX, "ODROID-X") + /* Maintainer: Dongjin Kim */ + .atag_offset = 0x100, + .init_irq = exynos4_init_irq, + .map_io = hkdk4412_map_io, + .handle_irq = gic_handle_irq, + .init_machine = hkdk4412_machine_init, + .init_late = exynos_init_late, + .timer = &exynos4_timer, + .restart = exynos4_restart, + .reserve = &hkdk4412_reserve, +MACHINE_END diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types index 2997e56ce0ddcf..e80ed9f16693f0 100644 --- a/arch/arm/tools/mach-types +++ b/arch/arm/tools/mach-types @@ -1206,3 +1206,4 @@ baileys MACH_BAILEYS BAILEYS 4169 familybox MACH_FAMILYBOX FAMILYBOX 4170 ensemble_mx35 MACH_ENSEMBLE_MX35 ENSEMBLE_MX35 4171 sc_sps_1 MACH_SC_SPS_1 SC_SPS_1 4172 +odroidx MACH_ODROIDX ODROIDX 4289 From f55cc8bcc9ae381dc5ba54a1cbc0f95fa68da038 Mon Sep 17 00:00:00 2001 From: Dongjin Kim Date: Tue, 24 Jul 2012 16:01:34 +0900 Subject: [PATCH 02/50] ARM: EXYNOS: Enable multiple cores on Exynos4 This patch enables CPU cores on Exynos4, on Exynos4412 secondary CPU cores are power-gated, therefore we must turn on the CPU cores on the system boot. Shows below log message on boot. [ 0.045000] CPU: Testing write buffer coherency: ok [ 0.045000] CPU0: thread -1, cpu 0, socket 10, mpidr 80000a00 [ 0.045000] hw perfevents: enabled with ARMv7 Cortex-A9 PMU driver, 7 counters e [ 0.045000] Setting up static identity map for 0x40370790 - 0x403707e8 [ 0.045000] L310 cache controller enabled [ 0.045000] l2x0: 16 ways, CACHE_ID 0x4100c4c8, AUX_CTRL 0x7e470001, Cache sizeB [ 0.070000] CPU1: Booted secondary processor [ 0.090000] CPU1: thread -1, cpu 1, socket 10, mpidr 80000a01 [ 0.090000] CPU1: Unknown IPI message 0x1 [ 0.100000] CPU2: Booted secondary processor [ 0.120000] CPU2: thread -1, cpu 2, socket 10, mpidr 80000a02 [ 0.120000] CPU2: Unknown IPI message 0x1 [ 0.130000] CPU3: Booted secondary processor [ 0.150000] CPU3: thread -1, cpu 3, socket 10, mpidr 80000a03 [ 0.150000] CPU3: Unknown IPI message 0x1 [ 0.150000] Brought up 4 CPUs [ 0.150000] SMP: Total of 4 processors activated (7969.17 BogoMIPS). Change-Id: I61615c5b719d3646698f114fc3777eb304694099 Signed-off-by: Dongjin Kim --- arch/arm/mach-exynos/hotplug.c | 4 +- arch/arm/mach-exynos/include/mach/regs-pmu.h | 11 +- arch/arm/mach-exynos/platsmp.c | 100 ++++++++++++------- 3 files changed, 76 insertions(+), 39 deletions(-) diff --git a/arch/arm/mach-exynos/hotplug.c b/arch/arm/mach-exynos/hotplug.c index 9c17a0a43858d8..cd534973b0f343 100644 --- a/arch/arm/mach-exynos/hotplug.c +++ b/arch/arm/mach-exynos/hotplug.c @@ -66,8 +66,8 @@ static inline void platform_do_lowpower(unsigned int cpu, int *spurious) for (;;) { /* make cpu1 to be turned off at next WFI command */ - if (cpu == 1) - __raw_writel(0, S5P_ARM_CORE1_CONFIGURATION); + if ((cpu >= 1) && (cpu < num_possible_cpus())) + __raw_writel(0, S5P_ARM_CORE_CONFIGURATION(cpu)); /* * here's the WFI diff --git a/arch/arm/mach-exynos/include/mach/regs-pmu.h b/arch/arm/mach-exynos/include/mach/regs-pmu.h index d4e392b811a315..0bb21e2c11c9e7 100644 --- a/arch/arm/mach-exynos/include/mach/regs-pmu.h +++ b/arch/arm/mach-exynos/include/mach/regs-pmu.h @@ -123,10 +123,15 @@ #define S5P_GPS_ALIVE_LOWPWR S5P_PMUREG(0x13A0) #define S5P_ARM_CORE0_CONFIGURATION S5P_PMUREG(0x2000) +#define S5P_ARM_CORE0_STATUS S5P_PMUREG(0x2004) #define S5P_ARM_CORE0_OPTION S5P_PMUREG(0x2008) -#define S5P_ARM_CORE1_CONFIGURATION S5P_PMUREG(0x2080) -#define S5P_ARM_CORE1_STATUS S5P_PMUREG(0x2084) -#define S5P_ARM_CORE1_OPTION S5P_PMUREG(0x2088) + +#define S5P_ARM_CORE_OPTION(_nr) (S5P_ARM_CORE0_OPTION + ((_nr) * 0x80)) +#define S5P_ARM_CORE_STATUS(_nr) (S5P_ARM_CORE0_STATUS + ((_nr) * 0x80)) +#define S5P_ARM_CORE_CONFIGURATION(_nr) \ + (S5P_ARM_CORE0_CONFIGURATION + ((_nr) * 0x80)) + +#define S5P_CORE_OPTION_DIS (1 << 8) #define S5P_ARM_COMMON_OPTION S5P_PMUREG(0x2408) #define S5P_TOP_PWR_OPTION S5P_PMUREG(0x2C48) diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index 36c3984aaa4790..68ca26fe9fa221 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c @@ -34,9 +34,6 @@ extern void exynos4_secondary_startup(void); -#define CPU1_BOOT_REG (samsung_rev() == EXYNOS4210_REV_1_1 ? \ - S5P_INFORM5 : S5P_VA_SYSRAM) - /* * control for which core is the next to come out of the secondary * boot "holding pen" @@ -59,6 +56,9 @@ static void write_pen_release(int val) static void __iomem *scu_base_addr(void) { + if (soc_is_exynos5250()) + return 0; + return (void __iomem *)(S5P_VA_SCU); } @@ -86,9 +86,41 @@ void __cpuinit platform_secondary_init(unsigned int cpu) spin_unlock(&boot_lock); } +static int exynos_power_up_cpu(unsigned int cpu) +{ + unsigned long timeout; + unsigned int val; + void __iomem *power_base = S5P_ARM_CORE_CONFIGURATION(cpu); + + val = __raw_readl(power_base); + if (!(val & S5P_CORE_LOCAL_PWR_EN)) { + __raw_writel(S5P_CORE_LOCAL_PWR_EN, power_base); + + timeout = 10; + + /* wait max 10 ms until cpu is on */ + while ((__raw_readl(power_base + 0x4) + & S5P_CORE_LOCAL_PWR_EN) != S5P_CORE_LOCAL_PWR_EN) { + if (timeout-- == 0) + break; + + mdelay(1); + } + + if (timeout == 0) { + pr_err("cpu%d power enable failed", cpu); + return -ETIMEDOUT; + } + } + + return 0; +} + int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) { unsigned long timeout; + void __iomem *boot_base; + int ret; /* * Set synchronisation state between this boot processor @@ -96,6 +128,12 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) */ spin_lock(&boot_lock); + ret = exynos_power_up_cpu(cpu); + if (ret) { + spin_unlock(&boot_lock); + return ret; + } + /* * The secondary processor is waiting to be released from * the holding pen - release it, then wait for it to flag @@ -106,39 +144,33 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) */ write_pen_release(cpu_logical_map(cpu)); - if (!(__raw_readl(S5P_ARM_CORE1_STATUS) & S5P_CORE_LOCAL_PWR_EN)) { - __raw_writel(S5P_CORE_LOCAL_PWR_EN, - S5P_ARM_CORE1_CONFIGURATION); - - timeout = 10; - - /* wait max 10 ms until cpu1 is on */ - while ((__raw_readl(S5P_ARM_CORE1_STATUS) - & S5P_CORE_LOCAL_PWR_EN) != S5P_CORE_LOCAL_PWR_EN) { - if (timeout-- == 0) - break; - - mdelay(1); - } - - if (timeout == 0) { - printk(KERN_ERR "cpu1 power enable failed"); - spin_unlock(&boot_lock); - return -ETIMEDOUT; - } - } /* * Send the secondary CPU a soft interrupt, thereby causing * the boot monitor to read the system wide flags register, * and branch to the address found there. */ - timeout = jiffies + (1 * HZ); while (time_before(jiffies, timeout)) { smp_rmb(); + if (soc_is_exynos4210() && + (samsung_rev() == EXYNOS4210_REV_1_1)) + boot_base = S5P_INFORM5; + else + boot_base = S5P_VA_SYSRAM; + + if (soc_is_exynos4412()) + boot_base += (0x4 * cpu); + + /* + * Write the address of secondary startup into the + * system-wide flags register. The boot monitor waits + * until it receives a soft interrupt, and then the + * secondary CPU branches to this address. + */ __raw_writel(virt_to_phys(exynos4_secondary_startup), - CPU1_BOOT_REG); + boot_base); + gic_raise_softirq(cpumask_of(cpu), 1); if (pen_release == -1) @@ -186,15 +218,15 @@ void __init smp_init_cpus(void) void __init platform_smp_prepare_cpus(unsigned int max_cpus) { - if (!soc_is_exynos5250()) - scu_enable(scu_base_addr()); + int i; /* - * Write the address of secondary startup into the - * system-wide flags register. The boot monitor waits - * until it receives a soft interrupt, and then the - * secondary CPU branches to this address. + * Initialise the present map, which describes the set of CPUs + * actually populated at the present time. */ - __raw_writel(virt_to_phys(exynos4_secondary_startup), - CPU1_BOOT_REG); + for (i = 0; i < max_cpus; i++) + set_cpu_present(i, true); + + if (!soc_is_exynos5250()) + scu_enable(scu_base_addr()); } From 75c46fcbdfe1813eae5f0cfe4ab60348527b607a Mon Sep 17 00:00:00 2001 From: Dongjin Kim Date: Fri, 13 Jul 2012 16:51:49 +0900 Subject: [PATCH 03/50] ARM: EXYNOS: Add gpio functions for Exynos4412 GPIO functions are not registered for Exynos4412 yet, therefore exynos4_gpiolib_init() is added to initialize Exynos4412 SoC. Change-Id: I5945d94c6fbfc309ccf882eba067864a338c04ca Signed-off-by: Dongjin Kim --- drivers/gpio/gpio-samsung.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c index 92f7b2bb79d40c..6b312a97fe626a 100644 --- a/drivers/gpio/gpio-samsung.c +++ b/drivers/gpio/gpio-samsung.c @@ -2727,7 +2727,7 @@ static __init void exynos_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip, static __init void exynos4_gpiolib_init(void) { -#ifdef CONFIG_CPU_EXYNOS4210 +#ifdef CONFIG_ARCH_EXYNOS4 struct samsung_gpio_chip *chip; int i, nr_chips; void __iomem *gpio_base1, *gpio_base2, *gpio_base3; @@ -2816,7 +2816,7 @@ static __init void exynos4_gpiolib_init(void) iounmap(gpio_base1); err_ioremap1: return; -#endif /* CONFIG_CPU_EXYNOS4210 */ +#endif /* CONFIG_ARCH_EXYNOS4 */ } static __init void exynos5_gpiolib_init(void) @@ -3014,6 +3014,8 @@ static __init int samsung_gpiolib_init(void) exynos4_gpiolib_init(); } else if (soc_is_exynos5250()) { exynos5_gpiolib_init(); + } else if (soc_is_exynos4212() || soc_is_exynos4412()) { + exynos4_gpiolib_init(); } else { WARN(1, "Unknown SoC in gpio-samsung, no GPIOs added\n"); return -ENODEV; From 6a590abbb4cfb886b8d484ccf405a812181c42eb Mon Sep 17 00:00:00 2001 From: Dongjin Kim Date: Sat, 28 Jul 2012 13:32:16 +0900 Subject: [PATCH 04/50] ARM: EXYNOS: Add USB HSIC device This patch support to control USB HSIC of EXYNOS4, edited based on Samsung's GT-i9100 ICS Opensource Update7. Change-Id: Ifba33c6a5166abf3644794eee6abe528bd71f521 Signed-off-by: Dongjin Kim --- arch/arm/mach-exynos/common.c | 5 + arch/arm/mach-exynos/include/mach/regs-pmu.h | 12 + .../mach-exynos/include/mach/regs-usb-phy.h | 97 ++++ arch/arm/mach-exynos/setup-usb-phy.c | 493 +++++++++++++----- drivers/usb/host/Kconfig | 14 + 5 files changed, 501 insertions(+), 120 deletions(-) diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c index 4eb39cdf75eab7..94d58af280057f 100644 --- a/arch/arm/mach-exynos/common.c +++ b/arch/arm/mach-exynos/common.c @@ -178,6 +178,11 @@ static struct map_desc exynos4_iodesc[] __initdata = { .pfn = __phys_to_pfn(EXYNOS4_PA_L2CC), .length = SZ_4K, .type = MT_DEVICE, + }, { + .virtual = (unsigned long)S5P_VA_GPIO2, + .pfn = __phys_to_pfn(EXYNOS4_PA_GPIO2), + .length = SZ_4K, + .type = MT_DEVICE, }, { .virtual = (unsigned long)S5P_VA_DMC0, .pfn = __phys_to_pfn(EXYNOS4_PA_DMC0), diff --git a/arch/arm/mach-exynos/include/mach/regs-pmu.h b/arch/arm/mach-exynos/include/mach/regs-pmu.h index 0bb21e2c11c9e7..d98c2fe6124a5c 100644 --- a/arch/arm/mach-exynos/include/mach/regs-pmu.h +++ b/arch/arm/mach-exynos/include/mach/regs-pmu.h @@ -185,6 +185,15 @@ #define S5P_PMU_LCD1_CONF S5P_PMUREG(0x3CA0) /* Only for EXYNOS4x12 */ +#define S5P_USB_PHY_CONTROL S5P_PMUREG(0x0704) +#define S5P_USB_PHY_ENABLE (0x1 << 0) + +#define S5P_HSIC_1_PHY_CONTROL S5P_PMUREG(0x0708) +#define S5P_HSIC_1_PHY_ENABLE (0x1 << 0) + +#define S5P_HSIC_2_PHY_CONTROL S5P_PMUREG(0x070C) +#define S5P_HSIC_2_PHY_ENABLE (0x1 << 0) + #define S5P_ISP_ARM_LOWPWR S5P_PMUREG(0x1050) #define S5P_DIS_IRQ_ISP_ARM_LOCAL_LOWPWR S5P_PMUREG(0x1054) #define S5P_DIS_IRQ_ISP_ARM_CENTRAL_LOWPWR S5P_PMUREG(0x1058) @@ -242,6 +251,9 @@ #define EXYNOS5_SYS_WDTRESET (1 << 20) +#define EXYNOS5_USBDEV_PHY_CONTROL S5P_PMUREG(0x0704) +#define EXYNOS5_USBHOST_PHY_CONTROL S5P_PMUREG(0x0708) + #define EXYNOS5_ARM_CORE0_SYS_PWR_REG S5P_PMUREG(0x1000) #define EXYNOS5_DIS_IRQ_ARM_CORE0_LOCAL_SYS_PWR_REG S5P_PMUREG(0x1004) #define EXYNOS5_DIS_IRQ_ARM_CORE0_CENTRAL_SYS_PWR_REG S5P_PMUREG(0x1008) diff --git a/arch/arm/mach-exynos/include/mach/regs-usb-phy.h b/arch/arm/mach-exynos/include/mach/regs-usb-phy.h index 07277735252e39..79021a07ff2b9f 100644 --- a/arch/arm/mach-exynos/include/mach/regs-usb-phy.h +++ b/arch/arm/mach-exynos/include/mach/regs-usb-phy.h @@ -43,6 +43,43 @@ #define EXYNOS4210_CLKSEL_12M (0x2 << 0) #define EXYNOS4210_CLKSEL_24M (0x3 << 0) +#define EXYNOS4210_HSIC1_NORMAL_MASK (0x3 << 11) +#define EXYNOS4210_HSIC1_SLEEP (1 << 12) +#define EXYNOS4210_HSIC1_FORCE_SUSPEND (1 << 11) +#define EXYNOS4210_HSIC0_NORMAL_MASK (0x3 << 9) +#define EXYNOS4210_HSIC0_SLEEP (1 << 10) +#define EXYNOS4210_HSIC0_FORCE_SUSPEND (1 << 9) + +#define EXYNOS4210_HOST_LINK_PORT_SWRST_MASK (0xf << 6) +#define EXYNOS4210_HOST_LINK_PORT2_SWRST (1 << 9) +#define EXYNOS4210_HOST_LINK_PORT1_SWRST (1 << 8) +#define EXYNOS4210_HOST_LINK_PORT0_SWRST (1 << 7) +#define EXYNOS4210_HOST_LINK_ALL_SWRST (1 << 6) +#define EXYNOS4210_PHY1_SWRST_MASK (0x7 << 3) +#define EXYNOS4210_PHY1_HSIC_SWRST (1 << 5) +#define EXYNOS4210_PHY1_STD_SWRST (1 << 4) +#define EXYNOS4210_PHY1_ALL_SWRST (1 << 3) + +#define EXYNOS4X12_HSIC1_NORMAL_MASK (0x7 << 12) +#define EXYNOS4X12_HSIC1_SLEEP (1 << 14) +#define EXYNOS4X12_HSIC1_ANALOG_POWERDOWN (1 << 13) +#define EXYNOS4X12_HSIC1_FORCE_SUSPEND (1 << 12) +#define EXYNOS4X12_HSIC0_NORMAL_MASK (0x7 << 9) +#define EXYNOS4X12_HSIC0_SLEEP (1 << 11) +#define EXYNOS4X12_HSIC0_ANALOG_POWERDOWN (1 << 10) +#define EXYNOS4X12_HSIC0_FORCE_SUSPEND (1 << 9) + +#define EXYNOS4X12_HOST_LINK_PORT_SWRST_MASK (0xf << 7) +#define EXYNOS4X12_HOST_LINK_PORT2_SWRST (1 << 10) +#define EXYNOS4X12_HOST_LINK_PORT1_SWRST (1 << 9) +#define EXYNOS4X12_HOST_LINK_PORT0_SWRST (1 << 8) +#define EXYNOS4X12_HOST_LINK_ALL_SWRST (1 << 7) +#define EXYNOS4X12_PHY1_SWRST_MASK (0xf << 3) +#define EXYNOS4X12_PHY1_HSIC1_SWRST (1 << 6) +#define EXYNOS4X12_PHY1_HSIC0_SWRST (1 << 5) +#define EXYNOS4X12_PHY1_SWRST (1 << 4) +#define EXYNOS4X12_HOST_PHY_SWRST (1 << 3) + #define EXYNOS4X12_CLKSEL_MASK (0x7 << 0) #define EXYNOS4X12_CLKSEL_9600K (0x0 << 0) #define EXYNOS4X12_CLKSEL_10M (0x1 << 0) @@ -71,4 +108,64 @@ #define EXYNOS4_PHY1CON EXYNOS4_HSOTG_PHYREG(0x34) #define FPENABLEN (1 << 0) +/* For Exynos5 */ +#define EXYNOS5_PHY_HOST_CTRL0 EXYNOS4_HSOTG_PHYREG(0x00) +#define HOST_CTRL0_PHYSWRSTALL (0x1 << 31) +#define HOST_CTRL0_REFCLKSEL(val) (val << 19) +#define EXYNOS5_CLKSEL_50M (0x7) +#define EXYNOS5_CLKSEL_24M (0x5) +#define EXYNOS5_CLKSEL_20M (0x4) +#define EXYNOS5_CLKSEL_19200K (0x3) +#define EXYNOS5_CLKSEL_12M (0x2) +#define EXYNOS5_CLKSEL_10M (0x1) +#define EXYNOS5_CLKSEL_9600K (0x0) +#define HOST_CTRL0_CLKSEL_SHIFT (16) +#define HOST_CTRL0_FSEL_MASK (0x7 << 16) + +#define HOST_CTRL0_COMMONON_N (0x1 << 9) +#define HOST_CTRL0_SIDDQ (0x1 << 6) +#define HOST_CTRL0_FORCESLEEP (0x1 << 5) +#define HOST_CTRL0_FORCESUSPEND (0x1 << 4) +#define HOST_CTRL0_WORDINTERFACE (0x1 << 3) +#define HOST_CTRL0_UTMISWRST (0x1 << 2) +#define HOST_CTRL0_LINKSWRST (0x1 << 1) +#define HOST_CTRL0_PHYSWRST (0x1 << 0) + +#define EXYNOS5_PHY_HOST_TUNE0 EXYNOS4_HSOTG_PHYREG(0x04) +#define EXYNOS5_PHY_HOST_TEST0 EXYNOS4_HSOTG_PHYREG(0x08) + +#define EXYNOS5_PHY_HSIC_CTRL1 EXYNOS4_HSOTG_PHYREG(0x10) +#define EXYNOS5_PHY_HSIC_CTRL2 EXYNOS4_HSOTG_PHYREG(0x20) +#define HSIC_CTRL_REFCLKSEL(val) ((val&0x3) << 23) +#define HSIC_CTRL_REFCLKDIV(val) ((val&0x7f) << 16) +#define HSIC_CTRL_SIDDQ (0x1 << 6) +#define HSIC_CTRL_FORCESLEEP (0x1 << 5) +#define HSIC_CTRL_FORCESUSPEND (0x1 << 4) +#define HSIC_CTRL_WORDINTERFACE (0x1 << 3) +#define HSIC_CTRL_UTMISWRST (0x1 << 2) +#define HSIC_CTRL_PHYSWRST (0x1 << 0) + +#define EXYNOS5_PHY_HOST_EHCICTRL EXYNOS4_HSOTG_PHYREG(0x30) +#define EHCICTRL_ENAINCRXALIGN (0x1 << 29) +#define EHCICTRL_ENAINCR4 (0x1 << 28) +#define EHCICTRL_ENAINCR8 (0x1 << 27) +#define EHCICTRL_ENAINCR16 (0x1 << 26) + +#define EXYNOS5_PHY_HOST_OHCICTRL EXYNOS4_HSOTG_PHYREG(0x34) + +#define EXYNOS5_PHY_OTG_SYS EXYNOS4_HSOTG_PHYREG(0x38) +#define OTG_SYS_PHYLINK_SW_RESET (0x1 << 14) +#define OTG_SYS_LINK_SW_RST_UOTG (0x1 << 13) +#define OTG_SYS_PHY0_SW_RST (0x1 << 12) +#define OTG_SYS_REF_CLK_SEL(val) ((val&0x3) << 9) +#define OTG_SYS_REF_CLK_SEL_MASK (0x3 << 9) +#define OTG_SYS_IP_PULLUP_UOTG (0x1 << 8) +#define OTG_SYS_COMMON_ON (0x1 << 7) +#define OTG_SYS_CLKSEL_SHIFT (4) +#define OTG_SYS_CTRL0_FSEL_MASK (0x7 << 4) +#define OTG_SYS_FORCE_SLEEP (0x1 << 3) +#define OTG_SYS_OTGDISABLE (0x1 << 2) +#define OTG_SYS_SIDDQ_UOTG (0x1 << 1) +#define OTG_SYS_FORCE_SUSPEND (0x1 << 0) + #endif /* __PLAT_S5P_REGS_USB_PHY_H */ diff --git a/arch/arm/mach-exynos/setup-usb-phy.c b/arch/arm/mach-exynos/setup-usb-phy.c index b81cc569a8ddab..c7257458f74a83 100644 --- a/arch/arm/mach-exynos/setup-usb-phy.c +++ b/arch/arm/mach-exynos/setup-usb-phy.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2011 Samsung Electronics Co.Ltd + * Author: Yulgon Kim * Author: Joonyoung Shim * * This program is free software; you can redistribute it and/or modify it @@ -19,205 +20,457 @@ #include #include +#define ETC6PUD (S5P_VA_GPIO2 + 0x228) +#define EXYNOS4_USB_CFG (S3C_VA_SYS + 0x21C) + +#define PHY_ENABLE (1 << 0) +#define PHY_DISABLE (0) + +enum usb_host_type { + HOST_PHY_EHCI = (0x1 << 0), + HOST_PHY_OHCI = (0x1 << 1), +}; + +enum usb_phy_type { + USB_PHY = (0x1 << 0), + USB_PHY0 = (0x1 << 0), + USB_PHY1 = (0x1 << 1), + USB_PHY_HSIC0 = (0x1 << 1), + USB_PHY_HSIC1 = (0x1 << 2), +}; + static atomic_t host_usage; +static DEFINE_MUTEX(phy_lock); +static struct clk *phy_clk; static int exynos4_usb_host_phy_is_on(void) { return (readl(EXYNOS4_PHYPWR) & PHY1_STD_ANALOG_POWERDOWN) ? 0 : 1; } -static void exynos4210_usb_phy_clkset(struct platform_device *pdev) +static int exynos4_usb_phy20_is_on(void) { - struct clk *xusbxti_clk; - u32 phyclk; + return exynos4_usb_host_phy_is_on(); +} + +static int exynos_usb_phy_clock_enable(struct platform_device *pdev) +{ + int err; + + if (!phy_clk) { + if (soc_is_exynos4210() || + soc_is_exynos4212() || soc_is_exynos4412()) + phy_clk = clk_get(&pdev->dev, "otg"); + else + phy_clk = clk_get(&pdev->dev, "usbhost"); + + if (IS_ERR(phy_clk)) { + dev_err(&pdev->dev, "Failed to get phy clock\n"); + return PTR_ERR(phy_clk); + } + } + + err = clk_enable(phy_clk); + + return err; +} - xusbxti_clk = clk_get(&pdev->dev, "xusbxti"); - if (xusbxti_clk && !IS_ERR(xusbxti_clk)) { - if (soc_is_exynos4210()) { - /* set clock frequency for PLL */ - phyclk = readl(EXYNOS4_PHYCLK) & ~EXYNOS4210_CLKSEL_MASK; - - switch (clk_get_rate(xusbxti_clk)) { - case 12 * MHZ: - phyclk |= EXYNOS4210_CLKSEL_12M; - break; - case 48 * MHZ: - phyclk |= EXYNOS4210_CLKSEL_48M; - break; - default: - case 24 * MHZ: - phyclk |= EXYNOS4210_CLKSEL_24M; - break; - } - writel(phyclk, EXYNOS4_PHYCLK); - } else if (soc_is_exynos4212() || soc_is_exynos4412()) { - /* set clock frequency for PLL */ - phyclk = readl(EXYNOS4_PHYCLK) & ~EXYNOS4X12_CLKSEL_MASK; - - switch (clk_get_rate(xusbxti_clk)) { - case 9600 * KHZ: - phyclk |= EXYNOS4X12_CLKSEL_9600K; - break; - case 10 * MHZ: - phyclk |= EXYNOS4X12_CLKSEL_10M; - break; - case 12 * MHZ: - phyclk |= EXYNOS4X12_CLKSEL_12M; - break; - case 19200 * KHZ: - phyclk |= EXYNOS4X12_CLKSEL_19200K; - break; - case 20 * MHZ: - phyclk |= EXYNOS4X12_CLKSEL_20M; - break; - default: - case 24 * MHZ: - /* default reference clock */ - phyclk |= EXYNOS4X12_CLKSEL_24M; - break; - } - writel(phyclk, EXYNOS4_PHYCLK); +static int exynos_usb_phy_clock_disable(struct platform_device *pdev) +{ + if (!phy_clk) { + if (soc_is_exynos4210() || + soc_is_exynos4212() || soc_is_exynos4412()) + phy_clk = clk_get(&pdev->dev, "otg"); + else + phy_clk = clk_get(&pdev->dev, "usbhost"); + if (IS_ERR(phy_clk)) { + dev_err(&pdev->dev, "Failed to get phy clock\n"); + return PTR_ERR(phy_clk); } - clk_put(xusbxti_clk); } + + clk_disable(phy_clk); + + return 0; } -static int exynos4210_usb_phy0_init(struct platform_device *pdev) +static u32 exynos_usb_phy_set_clock(struct platform_device *pdev) { + struct clk *ref_clk; + u32 refclk_freq = 0; + + if (soc_is_exynos4210() || soc_is_exynos4212() || soc_is_exynos4412()) + ref_clk = clk_get(&pdev->dev, "xusbxti"); + else + ref_clk = clk_get(&pdev->dev, "ext_xtal"); + + if (IS_ERR(ref_clk)) { + dev_err(&pdev->dev, "Failed to get reference clock\n"); + return PTR_ERR(ref_clk); + } + + if (soc_is_exynos4210()) { + switch (clk_get_rate(ref_clk)) { + case 12 * MHZ: + refclk_freq = EXYNOS4210_CLKSEL_12M; + break; + case 48 * MHZ: + refclk_freq = EXYNOS4210_CLKSEL_48M; + break; + case 24 * MHZ: + default: + /* default reference clock */ + refclk_freq = EXYNOS4210_CLKSEL_24M; + break; + } + } else if (soc_is_exynos4212() | soc_is_exynos4412()) { + switch (clk_get_rate(ref_clk)) { + case 96 * 100000: + refclk_freq = EXYNOS4X12_CLKSEL_9600K; + break; + case 10 * MHZ: + refclk_freq = EXYNOS4X12_CLKSEL_10M; + break; + case 12 * MHZ: + refclk_freq = EXYNOS4X12_CLKSEL_12M; + break; + case 192 * 100000: + refclk_freq = EXYNOS4X12_CLKSEL_19200K; + break; + case 20 * MHZ: + refclk_freq = EXYNOS4X12_CLKSEL_20M; + break; + case 24 * MHZ: + default: + /* default reference clock */ + refclk_freq = EXYNOS4X12_CLKSEL_24M; + break; + } + } else { + switch (clk_get_rate(ref_clk)) { + case 96 * 100000: + refclk_freq = EXYNOS5_CLKSEL_9600K; + break; + case 10 * MHZ: + refclk_freq = EXYNOS5_CLKSEL_10M; + break; + case 12 * MHZ: + refclk_freq = EXYNOS5_CLKSEL_12M; + break; + case 192 * 100000: + refclk_freq = EXYNOS5_CLKSEL_19200K; + break; + case 20 * MHZ: + refclk_freq = EXYNOS5_CLKSEL_20M; + break; + case 50 * MHZ: + refclk_freq = EXYNOS5_CLKSEL_50M; + break; + case 24 * MHZ: + default: + /* default reference clock */ + refclk_freq = EXYNOS5_CLKSEL_24M; + break; + } + } + clk_put(ref_clk); + + return refclk_freq; +} + +static void exynos_usb_phy_control(enum usb_phy_type phy_type , int on) +{ + if (soc_is_exynos4210()) { + if (phy_type & USB_PHY0) + writel(on, S5P_USBDEVICE_PHY_CONTROL); + if (phy_type & USB_PHY1) + writel(on, S5P_USBHOST_PHY_CONTROL); + } else if (soc_is_exynos4212() | soc_is_exynos4412()) { + if (phy_type & USB_PHY) + writel(on, S5P_USB_PHY_CONTROL); +#ifdef CONFIG_USB_S5P_HSIC0 + if (phy_type & USB_PHY_HSIC0) + writel(on, S5P_HSIC_1_PHY_CONTROL); +#endif +#ifdef CONFIG_USB_S5P_HSIC1 + if (phy_type & USB_PHY_HSIC1) + writel(on, S5P_HSIC_2_PHY_CONTROL); +#endif + } else { + if (phy_type & USB_PHY0) + writel(on, EXYNOS5_USBDEV_PHY_CONTROL); + if (phy_type & USB_PHY1) + writel(on, EXYNOS5_USBHOST_PHY_CONTROL); + } +} + +static int exynos4_usb_phy0_init(struct platform_device *pdev) +{ + u32 phypwr; + u32 phyclk; u32 rstcon; - writel(readl(S5P_USBDEVICE_PHY_CONTROL) | S5P_USBDEVICE_PHY_ENABLE, - S5P_USBDEVICE_PHY_CONTROL); + exynos_usb_phy_control(USB_PHY0, PHY_ENABLE); - exynos4210_usb_phy_clkset(pdev); + /* set clock frequency for PLL */ + phyclk = exynos_usb_phy_set_clock(pdev); + phyclk &= ~(PHY0_COMMON_ON_N); + writel(phyclk, EXYNOS4_PHYCLK); - /* set to normal PHY0 */ - writel((readl(EXYNOS4_PHYPWR) & ~PHY0_NORMAL_MASK), EXYNOS4_PHYPWR); + /* set to normal of PHY0 */ + phypwr = readl(EXYNOS4_PHYPWR) & ~PHY0_NORMAL_MASK; + writel(phypwr, EXYNOS4_PHYPWR); - /* reset PHY0 and Link */ + /* reset all ports of both PHY and Link */ rstcon = readl(EXYNOS4_RSTCON) | PHY0_SWRST_MASK; writel(rstcon, EXYNOS4_RSTCON); udelay(10); - rstcon &= ~PHY0_SWRST_MASK; writel(rstcon, EXYNOS4_RSTCON); return 0; } -static int exynos4210_usb_phy0_exit(struct platform_device *pdev) +static int exynos4_usb_phy0_exit(struct platform_device *pdev) { - writel((readl(EXYNOS4_PHYPWR) | PHY0_ANALOG_POWERDOWN | - PHY0_OTG_DISABLE), EXYNOS4_PHYPWR); + /* unset to normal of PHY0 */ + writel((readl(EXYNOS4_PHYPWR) | PHY0_NORMAL_MASK), + EXYNOS4_PHYPWR); - writel(readl(S5P_USBDEVICE_PHY_CONTROL) & ~S5P_USBDEVICE_PHY_ENABLE, - S5P_USBDEVICE_PHY_CONTROL); + exynos_usb_phy_control(USB_PHY0, PHY_DISABLE); return 0; } -static int exynos4210_usb_phy1_init(struct platform_device *pdev) +static int exynos4_usb_phy1_init(struct platform_device *pdev) { - struct clk *otg_clk; + u32 phypwr; + u32 phyclk; u32 rstcon; - int err; atomic_inc(&host_usage); - otg_clk = clk_get(&pdev->dev, "otg"); - if (IS_ERR(otg_clk)) { - dev_err(&pdev->dev, "Failed to get otg clock\n"); - return PTR_ERR(otg_clk); + if (exynos4_usb_host_phy_is_on()) { + dev_err(&pdev->dev, "Already power on PHY\n"); + return 0; } - err = clk_enable(otg_clk); - if (err) { - clk_put(otg_clk); - return err; - } + /* + * set XuhostOVERCUR to in-active by controlling ET6PUD[15:14] + * 0x0 : pull-up/down disabled + * 0x1 : pull-down enabled + * 0x2 : reserved + * 0x3 : pull-up enabled + */ + writel((__raw_readl(ETC6PUD) & ~(0x3 << 14)) | (0x3 << 14), + ETC6PUD); - if (exynos4_usb_host_phy_is_on()) - return 0; + exynos_usb_phy_control(USB_PHY1, PHY_ENABLE); - writel(readl(S5P_USBHOST_PHY_CONTROL) | S5P_USBHOST_PHY_ENABLE, - S5P_USBHOST_PHY_CONTROL); + /* set clock frequency for PLL */ + phyclk = exynos_usb_phy_set_clock(pdev); + phyclk &= ~(PHY1_COMMON_ON_N); + writel(phyclk, EXYNOS4_PHYCLK); - exynos4210_usb_phy_clkset(pdev); + /* set to normal HSIC 0 and 1 of PHY1 */ + phypwr = readl(EXYNOS4_PHYPWR); + phypwr &= ~(PHY1_STD_NORMAL_MASK + | EXYNOS4210_HSIC0_NORMAL_MASK); + writel(phypwr, EXYNOS4_PHYPWR); /* floating prevention logic: disable */ writel((readl(EXYNOS4_PHY1CON) | FPENABLEN), EXYNOS4_PHY1CON); - /* set to normal HSIC 0 and 1 of PHY1 */ - writel((readl(EXYNOS4_PHYPWR) & ~PHY1_HSIC_NORMAL_MASK), - EXYNOS4_PHYPWR); - - /* set to normal standard USB of PHY1 */ - writel((readl(EXYNOS4_PHYPWR) & ~PHY1_STD_NORMAL_MASK), EXYNOS4_PHYPWR); - /* reset all ports of both PHY and Link */ - rstcon = readl(EXYNOS4_RSTCON) | HOST_LINK_PORT_SWRST_MASK | - PHY1_SWRST_MASK; + rstcon = readl(EXYNOS4_RSTCON) + | EXYNOS4210_HOST_LINK_PORT_SWRST_MASK + | EXYNOS4210_PHY1_SWRST_MASK; writel(rstcon, EXYNOS4_RSTCON); udelay(10); - rstcon &= ~(HOST_LINK_PORT_SWRST_MASK | PHY1_SWRST_MASK); + rstcon &= ~(EXYNOS4210_HOST_LINK_PORT_SWRST_MASK + | EXYNOS4210_PHY1_SWRST_MASK); writel(rstcon, EXYNOS4_RSTCON); udelay(80); - clk_disable(otg_clk); - clk_put(otg_clk); + return 0; +} + +static int exynos4_usb_phy1_exit(struct platform_device *pdev) +{ + u32 phypwr; + + if (atomic_dec_return(&host_usage) > 0) { + dev_info(&pdev->dev, "still being used\n"); + return -EBUSY; + } + + phypwr = readl(EXYNOS4_PHYPWR) + | PHY1_STD_NORMAL_MASK + | EXYNOS4210_HSIC0_NORMAL_MASK; + writel(phypwr, EXYNOS4_PHYPWR); + + exynos_usb_phy_control(USB_PHY1, PHY_DISABLE); return 0; } -static int exynos4210_usb_phy1_exit(struct platform_device *pdev) +static int exynos4_usb_phy20_init(struct platform_device *pdev) { - struct clk *otg_clk; - int err; + u32 phypwr, phyclk, rstcon; - if (atomic_dec_return(&host_usage) > 0) - return 0; + atomic_inc(&host_usage); - otg_clk = clk_get(&pdev->dev, "otg"); - if (IS_ERR(otg_clk)) { - dev_err(&pdev->dev, "Failed to get otg clock\n"); - return PTR_ERR(otg_clk); + if (exynos4_usb_phy20_is_on()) { + dev_err(&pdev->dev, "Already power on PHY\n"); + return 0; } - err = clk_enable(otg_clk); - if (err) { - clk_put(otg_clk); - return err; + /* + * set XuhostOVERCUR to in-active by controlling ET6PUD[15:14] + * 0x0 : pull-up/down disabled + * 0x1 : pull-down enabled + * 0x2 : reserved + * 0x3 : pull-up enabled + */ + writel((__raw_readl(ETC6PUD) & ~(0x3 << 14)) | (0x3 << 14), + ETC6PUD); + + exynos_usb_phy_control(USB_PHY + | USB_PHY_HSIC0, + PHY_ENABLE); + + /* set clock frequency for PLL */ + phyclk = exynos_usb_phy_set_clock(pdev); + /* COMMON Block configuration during suspend */ + phyclk &= ~(PHY0_COMMON_ON_N | PHY1_COMMON_ON_N); + writel(phyclk, EXYNOS4_PHYCLK); + + /* set to normal of Device */ + phypwr = readl(EXYNOS4_PHYPWR) & ~PHY0_NORMAL_MASK; + writel(phypwr, EXYNOS4_PHYPWR); + + /* set to normal of Host */ + phypwr = readl(EXYNOS4_PHYPWR); + phypwr &= ~(PHY1_STD_NORMAL_MASK + | EXYNOS4X12_HSIC0_NORMAL_MASK); + writel(phypwr, EXYNOS4_PHYPWR); + + /* reset both PHY and Link of Device */ + rstcon = readl(EXYNOS4_RSTCON) | PHY0_SWRST_MASK; + writel(rstcon, EXYNOS4_RSTCON); + udelay(10); + rstcon &= ~PHY0_SWRST_MASK; + writel(rstcon, EXYNOS4_RSTCON); + + /* reset both PHY and Link of Host */ + rstcon = readl(EXYNOS4_RSTCON) + | EXYNOS4X12_HOST_LINK_PORT_SWRST_MASK + | EXYNOS4X12_PHY1_SWRST_MASK; + writel(rstcon, EXYNOS4_RSTCON); + udelay(10); + + rstcon &= ~(EXYNOS4X12_HOST_LINK_PORT_SWRST_MASK + | EXYNOS4X12_PHY1_SWRST_MASK); + writel(rstcon, EXYNOS4_RSTCON); + udelay(80); + + return 0; +} + +static int exynos4_usb_phy20_exit(struct platform_device *pdev) +{ + u32 phypwr; + + if (atomic_dec_return(&host_usage) > 0) { + dev_info(&pdev->dev, "still being used\n"); + return -EBUSY; } - writel((readl(EXYNOS4_PHYPWR) | PHY1_STD_ANALOG_POWERDOWN), + /* unset to normal of Device */ + writel((readl(EXYNOS4_PHYPWR) | PHY0_NORMAL_MASK), EXYNOS4_PHYPWR); - writel(readl(S5P_USBHOST_PHY_CONTROL) & ~S5P_USBHOST_PHY_ENABLE, - S5P_USBHOST_PHY_CONTROL); + /* unset to normal of Host */ + phypwr = readl(EXYNOS4_PHYPWR) + | PHY1_STD_NORMAL_MASK + | EXYNOS4X12_HSIC0_NORMAL_MASK; + writel(phypwr, EXYNOS4_PHYPWR); - clk_disable(otg_clk); - clk_put(otg_clk); + exynos_usb_phy_control(USB_PHY + | USB_PHY_HSIC0, + PHY_DISABLE); + + return 0; +} + +static int exynos_usb_dev_phy20_init(struct platform_device *pdev) +{ + if (soc_is_exynos4212() || soc_is_exynos4412()) + exynos4_usb_phy20_init(pdev); + + writel(0, EXYNOS4_USB_CFG); + + return 0; +} + +static int exynos_usb_dev_phy20_exit(struct platform_device *pdev) +{ + if (soc_is_exynos4212() || soc_is_exynos4412()) + exynos4_usb_phy20_exit(pdev); return 0; } int s5p_usb_phy_init(struct platform_device *pdev, int type) { - if (type == S5P_USB_PHY_DEVICE) - return exynos4210_usb_phy0_init(pdev); - else if (type == S5P_USB_PHY_HOST) - return exynos4210_usb_phy1_init(pdev); + int ret = -EINVAL; + + if (exynos_usb_phy_clock_enable(pdev)) + return ret; + + mutex_lock(&phy_lock); + if (type == S5P_USB_PHY_HOST) { + if (soc_is_exynos4210()) + ret = exynos4_usb_phy1_init(pdev); + else if (soc_is_exynos4212() || soc_is_exynos4412()) + ret = exynos4_usb_phy20_init(pdev); + } else if (type == S5P_USB_PHY_DEVICE) { + if (soc_is_exynos4210()) + ret = exynos4_usb_phy0_init(pdev); + else + ret = exynos_usb_dev_phy20_init(pdev); + } + + mutex_unlock(&phy_lock); + exynos_usb_phy_clock_disable(pdev); - return -EINVAL; + return ret; } int s5p_usb_phy_exit(struct platform_device *pdev, int type) { - if (type == S5P_USB_PHY_DEVICE) - return exynos4210_usb_phy0_exit(pdev); - else if (type == S5P_USB_PHY_HOST) - return exynos4210_usb_phy1_exit(pdev); + int ret = -EINVAL; + + if (exynos_usb_phy_clock_enable(pdev)) + return ret; + + mutex_lock(&phy_lock); + + if (type == S5P_USB_PHY_HOST) { + if (soc_is_exynos4210()) + ret = exynos4_usb_phy1_exit(pdev); + else if (soc_is_exynos4212() || soc_is_exynos4412()) + ret = exynos4_usb_phy20_exit(pdev); + } else if (type == S5P_USB_PHY_DEVICE) { + if (soc_is_exynos4210()) + ret = exynos4_usb_phy0_exit(pdev); + else + ret = exynos_usb_dev_phy20_exit(pdev); + } + + mutex_unlock(&phy_lock); + exynos_usb_phy_clock_disable(pdev); - return -EINVAL; + return ret; } diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 075d2eca8108c5..5f69041f44efb4 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -199,6 +199,20 @@ config USB_EHCI_S5P help Enable support for the S5P SOC's on-chip EHCI controller. +config USB_S5P_HSIC0 + boolean "S5P HSIC0 support" + depends on USB_EHCI_HCD && PLAT_S5P && USB_EHCI_S5P + default n + help + Enable support for the S5P SOC's on-chip HSIC PHY. + +config USB_S5P_HSIC1 + boolean "S5P HSIC1 support" + depends on USB_EHCI_HCD && PLAT_S5P && USB_EHCI_S5P + default n + help + Enable support for the S5P SOC's on-chip HSIC PHY. + config USB_EHCI_MV bool "EHCI support for Marvell on-chip controller" depends on USB_EHCI_HCD && (ARCH_PXA || ARCH_MMP) From b9df8467a773528e5b8ef84b08e778583258db9d Mon Sep 17 00:00:00 2001 From: Dongjin Kim Date: Sat, 28 Jul 2012 03:35:00 +0900 Subject: [PATCH 05/50] USB: misc: Add USB3503 High-Speed Hub Controller This patch adds the USB3503 High-Speed Hub Controller driver. The driver does work properly if INT_N and HUB_CONNECT of USB3503 are tied to HIGH, only RESET_N is handled to switch its state Hub Communication Stage and Standby Stage. Otherwise USB3503 will not be detected or not able to scan USB devices connected. Change-Id: I4d057d5d1ca077ea3b2a78a0e0bc78bb20a82aa6 Signed-off-by: Dongjin Kim --- drivers/usb/misc/Kconfig | 5 + drivers/usb/misc/Makefile | 1 + drivers/usb/misc/usb3503.c | 475 ++++++++++++++++++++++++++ include/linux/platform_data/usb3503.h | 42 +++ 4 files changed, 523 insertions(+) create mode 100644 drivers/usb/misc/usb3503.c create mode 100644 include/linux/platform_data/usb3503.h diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 1bfcd02ebeb560..4ebf25031a3302 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -244,3 +244,8 @@ config USB_YUREX To compile this driver as a module, choose M here: the module will be called yurex. +config USB_HSIC_USB3503 + tristate "USB3503 HSIC to USB20 Driver" + depends on I2C + help + This option enables support for SMSC USB3503 HSIC to USB 2.0 Driver. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 796ce7ebccc8dc..3f2c2f8450acb1 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -25,5 +25,6 @@ obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o obj-$(CONFIG_USB_USS720) += uss720.o obj-$(CONFIG_USB_SEVSEG) += usbsevseg.o obj-$(CONFIG_USB_YUREX) += yurex.o +obj-$(CONFIG_USB_HSIC_USB3503) += usb3503.o obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c new file mode 100644 index 00000000000000..c15a7d67a8f7d0 --- /dev/null +++ b/drivers/usb/misc/usb3503.c @@ -0,0 +1,475 @@ +/* + * drivers/misc/usb3503.c - usb3503 usb hub driver + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ +#include +#include +#include +#include +#include +#include + +static int usb3503_register_write(struct i2c_client *i2c_dev, char reg, + char data) +{ + int ret; + char buf[2]; + struct i2c_msg msg[] = { + { + .addr = i2c_dev->addr, + .flags = 0, + .len = 2, + .buf = buf, + }, + }; + + buf[0] = reg; + buf[1] = data; + + ret = i2c_transfer(i2c_dev->adapter, msg, 1); + if (ret < 0) + pr_err(HUB_TAG "%s: reg: %x data: %x write failed\n", + __func__, reg, data); + + return ret; +} + +static int usb3503_register_read(struct i2c_client *i2c_dev, char reg, + char *data) +{ + int ret; + struct i2c_msg msgs[] = { + { + .addr = i2c_dev->addr, + .flags = 0, + .len = 1, + .buf = ®, + }, + { + .addr = i2c_dev->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = data, + }, + }; + + ret = i2c_transfer(i2c_dev->adapter, msgs, 2); + if (ret < 0) + pr_err(HUB_TAG "%s: reg: %x read failed\n", __func__, reg); + + return ret; +} + +void s5pv210_hsic_port1_power(int enable) +{ + /*TODO:*/ +} + +static int reg_write(struct i2c_client *i2c_dev, char reg, char req, int retry) +{ + int cnt = retry, err; + char data; + + pr_debug(HUB_TAG "%s: write %02X, data: %02x\n", __func__, reg, req); + do { + err = usb3503_register_write(i2c_dev, reg, req); + if (err < 0) + goto exit; + err = usb3503_register_read(i2c_dev, reg, &data); + if (err < 0) + goto exit; + } while (data != req && cnt--); +exit: + pr_info(HUB_TAG "%s: write %02X, req:%02x, val:%02x\n", __func__, reg, + req, data); + + return err; +} + +static int reg_update(struct i2c_client *i2c_dev, char reg, char req, int retry) +{ + int cnt = retry, err; + char data; + + pr_debug(HUB_TAG "%s: update %02X, data: %02x\n", __func__, reg, req); + do { + err = usb3503_register_read(i2c_dev, reg, &data); + if (err < 0) + goto exit; + pr_debug(HUB_TAG "%s: read %02X, data: %02x\n", __func__, reg, + data); + if ((data & req) == req) { + pr_debug(HUB_TAG "%s: aleady set data: %02x\n", + __func__, data); + break; + } + err = usb3503_register_write(i2c_dev, reg, data | req); + if (err < 0) + goto exit; + } while (cnt--); +exit: + pr_info(HUB_TAG "%s: update %02X, req:%02x, val:%02x\n", __func__, reg, + req, data); + return err; +} + +static int reg_clear(struct i2c_client *i2c_dev, char reg, char req, int retry) +{ + int cnt = retry, err; + char data; + + pr_debug(HUB_TAG "%s: clear %X, data %x\n", __func__, reg, req); + do { + err = usb3503_register_read(i2c_dev, reg, &data); + if (err < 0) + goto exit; + pr_debug(HUB_TAG "%s: read %02X, data %02x\n", __func__, reg, + data); + if (!(data & req)) { + pr_err(HUB_TAG "%s: aleady cleared data = %02x\n", + __func__, data); + break; + } + err = usb3503_register_write(i2c_dev, reg, data & ~req); + if (err < 0) + goto exit; + } while (cnt--); +exit: + pr_info(HUB_TAG "%s: clear %02X, req:%02x, val:%02x\n", __func__, reg, + req, data); + return err; +} + +static int usb3503_reset(struct usb3503_hubctl *hc, int reset) +{ + if ((hc == NULL) || (hc->reset_n == NULL)) + return -EINVAL; + + /* Assert RESET_N signal */ + hc->reset_n(reset); + + if (reset == 1) { + /* Wait RefClk when RESET_N is released, otherwise Hub will + * not transition to Hub Communication Stage. + */ + msleep(100); + } + + return 0; +} + +static int usb3503_set_mode(struct usb3503_hubctl *hc, int mode) +{ + int err = 0; + struct i2c_client *i2c_dev = hc->i2c_dev; + + pr_info(HUB_TAG "%s: mode = %d\n", __func__, mode); + + switch (mode) { + case USB3503_MODE_HUB: + usb3503_reset(hc, 1); + + /* SP_ILOCK: set connect_n, config_n for config */ + err = reg_write(i2c_dev, SP_ILOCK_REG, + (SPILOCK_CONNECT_N | SPILOCK_CONFIG_N), 3); + if (err < 0) { + pr_err(HUB_TAG "SP_ILOCK write fail err = %d\n", err); + goto exit; + } + +#ifdef USB3503_ES_VER +/* ES version issue + * USB3503 can't PLL power up under cold circumstance, so enable + * the Force suspend clock bit + */ + err = reg_update(i2c_dev, CFGP_REG, CFGP_CLKSUSP, 1); + if (err < 0) { + pr_err(HUB_TAG "CFGP update fail err = %d\n", err); + goto exit; + } +#endif + /* PDS : Port2,3 Disable For Self Powered Operation */ + err = reg_update(i2c_dev, PDS_REG, (PDS_PORT2 | PDS_PORT3), 1); + if (err < 0) { + pr_err(HUB_TAG "PDS update fail err = %d\n", err); + goto exit; + } + /* CFG1 : SELF_BUS_PWR -> Self-Powerd operation */ + err = reg_update(i2c_dev, CFG1_REG, CFG1_SELF_BUS_PWR, 1); + if (err < 0) { + pr_err(HUB_TAG "CFG1 update fail err = %d\n", err); + goto exit; + } + /* SP_LOCK: clear connect_n, config_n for hub connect */ + err = reg_clear(i2c_dev, SP_ILOCK_REG, + (SPILOCK_CONNECT_N | SPILOCK_CONFIG_N), 1); + if (err < 0) { + pr_err(HUB_TAG "SP_ILOCK clear err = %d\n", err); + goto exit; + } + hc->mode = mode; + + /* Should be enable the HSIC port1 */ + + break; + + case USB3503_MODE_STANDBY: + usb3503_reset(hc, 0); + hc->mode = mode; + break; + + default: + pr_err(HUB_TAG "%s: Invalid mode %d\n", __func__, mode); + err = -EINVAL; + goto exit; + break; + } +exit: + return err; +} + +/* sysfs for control */ +static ssize_t mode_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb3503_hubctl *hc = dev_get_drvdata(dev); + + if (hc->mode == USB3503_MODE_HUB) + return sprintf(buf, "%s", "hub"); + else if (hc->mode == USB3503_MODE_STANDBY) + return sprintf(buf, "%s", "standby"); + + return 0; +} + +static ssize_t mode_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct usb3503_hubctl *hc = dev_get_drvdata(dev); + + if (!strncmp(buf, "hub", 3)) { + /*usb3503_set_mode(hc, USB3503_MODE_HUB);*/ + if (hc->port_enable) + hc->port_enable(2, 1); + pr_debug(HUB_TAG "mode set to hub\n"); + } else if (!strncmp(buf, "standby", 7)) { + /*usb3503_set_mode(hc, USB3503_MODE_STANDBY);*/ + if (hc->port_enable) + hc->port_enable(2, 0); + pr_debug(HUB_TAG "mode set to standby\n"); + } + return size; +} +static DEVICE_ATTR(mode, 0664, mode_show, mode_store); + +#ifdef USB3503_SYSFS_DEBUG +static ssize_t read_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned addr; + char data; + int err; + struct usb3503_hubctl *hc = dev_get_drvdata(dev); + + err = sscanf(buf, "%x", &addr); + + err = usb3503_register_read(hc->i2c_dev, addr, &data); + if (err < 0) { + pr_err(HUB_TAG "register read fail\n"); + goto exit; + } + pr_info(HUB_TAG "%s: read 0x%x = 0x%x\n", __func__, addr, data); +exit: + return size; +} +static DEVICE_ATTR(read, 0664, NULL, read_store); + +static ssize_t write_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned addr, data; + int err; + struct usb3503_hubctl *hc = dev_get_drvdata(dev); + + err = sscanf(buf, "%x %x", &addr, &data); + pr_debug(HUB_TAG "%s: addr=%x, data=%x\n", __func__, addr, data); + + err = usb3503_register_write(hc->i2c_dev, addr, data); + if (err < 0) { + pr_err(HUB_TAG "register write fail\n"); + goto exit; + } + + err = usb3503_register_read(hc->i2c_dev, addr, (char *)&data); + if (err < 0) { + pr_err(HUB_TAG "register read fail\n"); + goto exit; + } + pr_info(HUB_TAG "%s: write 0x%x = 0x%x\n", __func__, addr, data); +exit: + return size; +} +static DEVICE_ATTR(write, 0664, NULL, write_store); + +static ssize_t reset_store( + struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + unsigned val; + int err; + struct usb3503_hubctl *hc = dev_get_drvdata(dev); + + err = sscanf(buf, "%x", &val); + pr_info(HUB_TAG "%s: val=%x\n", __func__, val); + + usb3503_reset(hc, val); + + return size; +} +static DEVICE_ATTR(reset, 0664, NULL, reset_store); +#endif /* end of USB3503_SYSFS_DEBUG */ + +int usb3503_suspend(struct i2c_client *client, pm_message_t mesg) +{ + struct usb3503_hubctl *hc = i2c_get_clientdata(client); + + /* Should be disable the HSIC port1 */ + + usb3503_reset(hc, 0); + pr_info(HUB_TAG "suspended\n"); + + return 0; +} + +int usb3503_resume(struct i2c_client *client) +{ + struct usb3503_hubctl *hc = i2c_get_clientdata(client); + + if (hc->mode == USB3503_MODE_HUB) + usb3503_set_mode(hc, USB3503_MODE_HUB); + + pr_info(HUB_TAG "resume mode=%s", (hc->mode == USB3503_MODE_HUB) ? + "hub" : "standby"); + + return 0; +} + +int usb3503_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + int err = 0; + struct usb3503_hubctl *hc; + struct usb3503_platform_data *pdata; + + pr_info(HUB_TAG "%s:%d\n", __func__, __LINE__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + err = -ENODEV; + goto exit; + } + + pdata = client->dev.platform_data; + if (pdata == NULL) { + pr_err(HUB_TAG "device's platform data is NULL!\n"); + err = -ENODEV; + goto exit; + } + + hc = kzalloc(sizeof(struct usb3503_hubctl), GFP_KERNEL); + if (!hc) { + pr_err(HUB_TAG "private data alloc fail\n"); + err = -ENOMEM; + goto exit; + } + hc->i2c_dev = client; + hc->reset_n = pdata->reset_n; + hc->port_enable = pdata->port_enable; + if (pdata->initial_mode) { + usb3503_set_mode(hc, pdata->initial_mode); + hc->mode = pdata->initial_mode; + } + /* For HSIC to USB brige with CMC221 + * export the hub_set_mode and private data to board modem + * it will be handled by PM scenaio. + */ + if (pdata->register_hub_handler) + pdata->register_hub_handler((void (*)(void))usb3503_set_mode, + (void *)hc); + + i2c_set_clientdata(client, hc); + + err = device_create_file(&client->dev, &dev_attr_mode); +#ifdef USB3503_SYSFS_DEBUG + err = device_create_file(&client->dev, &dev_attr_read); + err = device_create_file(&client->dev, &dev_attr_write); + err = device_create_file(&client->dev, &dev_attr_reset); +#endif + pr_info(HUB_TAG "%s: probed on %s mode\n", __func__, + (hc->mode == USB3503_MODE_HUB) ? "hub" : "standby"); +exit: + return err; +} + +static int usb3503_remove(struct i2c_client *client) +{ + struct usb3503_hubctl *hc = i2c_get_clientdata(client); + + pr_debug(HUB_TAG "%s\n", __func__); + kfree(hc); + + return 0; +} + +static const struct i2c_device_id usb3503_id[] = { + { USB3503_I2C_NAME, 0 }, + { } +}; + +static void usb3503_shutdown(struct i2c_client *client) +{ + struct usb3503_hubctl *hc = i2c_get_clientdata(client); + + pr_err(HUB_TAG "%s:\n", __func__); + mdelay(10); + usb3503_set_mode(hc, USB3503_MODE_STANDBY); +} + +static struct i2c_driver usb3503_driver = { + .probe = usb3503_probe, + .remove = usb3503_remove, + .suspend = usb3503_suspend, + .resume = usb3503_resume, + .shutdown = usb3503_shutdown, + .id_table = usb3503_id, + .driver = { + .name = USB3503_I2C_NAME, + }, +}; + +static int __init usb3503_init(void) +{ + pr_info(HUB_TAG "USB HUB driver init\n"); + return i2c_add_driver(&usb3503_driver); +} + +static void __exit usb3503_exit(void) +{ + pr_info(HUB_TAG "USB HUB driver exit\n"); + i2c_del_driver(&usb3503_driver); +} +module_init(usb3503_init); +module_exit(usb3503_exit); + +MODULE_DESCRIPTION("USB3503 USB HUB driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/platform_data/usb3503.h b/include/linux/platform_data/usb3503.h new file mode 100644 index 00000000000000..14944e3af45ea1 --- /dev/null +++ b/include/linux/platform_data/usb3503.h @@ -0,0 +1,42 @@ +#ifndef USB3503_H +#define USB3503_H + +#define USB3503_I2C_NAME "usb3503" +#define HUB_TAG "usb3503: " + +#define CFG1_REG 0x06 +#define CFG1_SELF_BUS_PWR (0x1 << 7) + +#define SP_ILOCK_REG 0xE7 +#define SPILOCK_CONNECT_N (0x1 << 1) +#define SPILOCK_CONFIG_N (0x1 << 0) + +#define CFGP_REG 0xEE +#define CFGP_CLKSUSP (0x1 << 7) + +#define PDS_REG 0x0A +#define PDS_PORT1 (0x1 << 1) +#define PDS_PORT2 (0x1 << 2) +#define PDS_PORT3 (0x1 << 3) + + +enum usb3503_mode { + USB3503_MODE_UNKNOWN, + USB3503_MODE_HUB, + USB3503_MODE_STANDBY, +}; + +struct usb3503_platform_data { + char initial_mode; + int (*reset_n)(int); + int (*register_hub_handler)(void (*)(void), void *); + int (*port_enable)(int, int); +}; + +struct usb3503_hubctl { + int mode; + int (*reset_n)(int); + int (*port_enable)(int, int); + struct i2c_client *i2c_dev; +}; +#endif From c391264a7098415eb0dbb5fe5739d46d587f26c6 Mon Sep 17 00:00:00 2001 From: Dongjin Kim Date: Sun, 29 Jul 2012 11:54:07 +0900 Subject: [PATCH 06/50] ODROID-X: usb: Add USB3503 platform device This patch adds the platform device of USB3503 and its pin configuration, INT_N and HUB_CONNECT are tied HIGH and RESET_N is LOW to be in Standby Stage. Change-Id: I61387906de3e7a25ee52f216c56e687f43e05ad2 Signed-off-by: Dongjin Kim --- arch/arm/mach-exynos/mach-hkdk4412.c | 39 +++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-exynos/mach-hkdk4412.c b/arch/arm/mach-exynos/mach-hkdk4412.c index 3b090ae97634f8..f65d968d447355 100644 --- a/arch/arm/mach-exynos/mach-hkdk4412.c +++ b/arch/arm/mach-exynos/mach-hkdk4412.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -823,11 +824,33 @@ static struct max77686_platform_data hkdk4412_max77686_info = { .buck4_voltage[7] = 900000, /* 0.9V */ }; +#if defined(CONFIG_USB_HSIC_USB3503) +#include + +static int usb3503_reset_n(int reset) +{ + gpio_set_value(EXYNOS4_GPX3(5), reset); + + return 0; +} + +static struct usb3503_platform_data usb3503_pdata = { + .initial_mode = USB3503_MODE_HUB, + .reset_n = usb3503_reset_n, +}; +#endif + static struct i2c_board_info hkdk4412_i2c_devs0[] __initdata = { { I2C_BOARD_INFO("max77686", (0x12 >> 1)), .platform_data = &hkdk4412_max77686_info, - } + }, +#if defined(CONFIG_USB_HSIC_USB3503) + { + I2C_BOARD_INFO("usb3503", (0x08)), + .platform_data = &usb3503_pdata, + }, +#endif }; static struct i2c_board_info hkdk4412_i2c_devs1[] __initdata = { @@ -933,6 +956,20 @@ static void __init hkdk4412_gpio_init(void) { /* Peripheral power enable (P3V3) */ gpio_request_one(EXYNOS4_GPA1(1), GPIOF_OUT_INIT_HIGH, "p3v3_en"); + +#if defined(CONFIG_USB_HSIC_USB3503) + /* INT_N must be asserted if interrupt is not used */ + gpio_request_one(EXYNOS4_GPX3(0), GPIOF_OUT_INIT_HIGH, + "usb3503_intn"); + + /* Hub will automatically transition to the Hub Communication Stage */ + gpio_request_one(EXYNOS4_GPX3(4), GPIOF_OUT_INIT_HIGH, + "usb3503_connect"); + + /* USB3503 - Standby Mode */ + gpio_request_one(EXYNOS4_GPX3(5), GPIOF_OUT_INIT_LOW, + "usb3503_reset_n"); +#endif } static void __init hkdk4412_machine_init(void) From d56d9efd20e3d7b928f864698de34d03a47ed968 Mon Sep 17 00:00:00 2001 From: Dongjin Kim Date: Sat, 28 Jul 2012 03:36:00 +0900 Subject: [PATCH 07/50] ODROID-X: lcd: ADD LG LP101WH1 LCD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LP101WH1 is 10.1″ LG LCD panel, supports 1366 X 768 resolution. Change-Id: If7cbf956ef953d0e32ae6b25ba7a0167e81f1447 Signed-off-by: Dongjin Kim --- arch/arm/mach-exynos/mach-hkdk4412.c | 75 +++++++++++++++++++++++++++- drivers/video/backlight/Kconfig | 7 +++ 2 files changed, 80 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-exynos/mach-hkdk4412.c b/arch/arm/mach-exynos/mach-hkdk4412.c index f65d968d447355..2fbc1a93622c02 100644 --- a/arch/arm/mach-exynos/mach-hkdk4412.c +++ b/arch/arm/mach-exynos/mach-hkdk4412.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -37,6 +38,9 @@ #include #include #include +#include +#include +#include