1/* 2 * linux/arch/arm/mach-s3c64xx/mach-smartq.c 3 * 4 * Copyright (C) 2010 Maurus Cuelenaere 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 */ 11 12#include <linux/delay.h> 13#include <linux/fb.h> 14#include <linux/gpio.h> 15#include <linux/init.h> 16#include <linux/platform_device.h> 17#include <linux/pwm.h> 18#include <linux/pwm_backlight.h> 19#include <linux/serial_core.h> 20#include <linux/serial_s3c.h> 21#include <linux/spi/spi_gpio.h> 22#include <linux/usb/gpio_vbus.h> 23#include <linux/platform_data/s3c-hsotg.h> 24 25#include <asm/mach-types.h> 26#include <asm/mach/map.h> 27 28#include <mach/map.h> 29#include <mach/regs-gpio.h> 30#include <mach/gpio-samsung.h> 31 32#include <plat/cpu.h> 33#include <plat/devs.h> 34#include <linux/platform_data/i2c-s3c2410.h> 35#include <plat/gpio-cfg.h> 36#include <linux/platform_data/hwmon-s3c.h> 37#include <linux/platform_data/usb-ohci-s3c2410.h> 38#include <plat/sdhci.h> 39#include <linux/platform_data/touchscreen-s3c2410.h> 40 41#include <video/platform_lcd.h> 42#include <plat/samsung-time.h> 43 44#include "common.h" 45#include "regs-modem.h" 46 47#define UCON S3C2410_UCON_DEFAULT 48#define ULCON (S3C2410_LCON_CS8 | S3C2410_LCON_PNONE) 49#define UFCON (S3C2410_UFCON_RXTRIG8 | S3C2410_UFCON_FIFOMODE) 50 51static struct s3c2410_uartcfg smartq_uartcfgs[] __initdata = { 52 [0] = { 53 .hwport = 0, 54 .flags = 0, 55 .ucon = UCON, 56 .ulcon = ULCON, 57 .ufcon = UFCON, 58 }, 59 [1] = { 60 .hwport = 1, 61 .flags = 0, 62 .ucon = UCON, 63 .ulcon = ULCON, 64 .ufcon = UFCON, 65 }, 66 [2] = { 67 .hwport = 2, 68 .flags = 0, 69 .ucon = UCON, 70 .ulcon = ULCON, 71 .ufcon = UFCON, 72 }, 73}; 74 75static void smartq_usb_host_powercontrol(int port, int to) 76{ 77 pr_debug("%s(%d, %d)\n", __func__, port, to); 78 79 if (port == 0) { 80 gpio_set_value(S3C64XX_GPL(0), to); 81 gpio_set_value(S3C64XX_GPL(1), to); 82 } 83} 84 85static irqreturn_t smartq_usb_host_ocirq(int irq, void *pw) 86{ 87 struct s3c2410_hcd_info *info = pw; 88 89 if (gpio_get_value(S3C64XX_GPL(10)) == 0) { 90 pr_debug("%s: over-current irq (oc detected)\n", __func__); 91 s3c2410_usb_report_oc(info, 3); 92 } else { 93 pr_debug("%s: over-current irq (oc cleared)\n", __func__); 94 s3c2410_usb_report_oc(info, 0); 95 } 96 97 return IRQ_HANDLED; 98} 99 100static void smartq_usb_host_enableoc(struct s3c2410_hcd_info *info, int on) 101{ 102 int ret; 103 104 /* This isn't present on a SmartQ 5 board */ 105 if (machine_is_smartq5()) 106 return; 107 108 if (on) { 109 ret = request_irq(gpio_to_irq(S3C64XX_GPL(10)), 110 smartq_usb_host_ocirq, 111 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 112 "USB host overcurrent", info); 113 if (ret != 0) 114 pr_err("failed to request usb oc irq: %d\n", ret); 115 } else { 116 free_irq(gpio_to_irq(S3C64XX_GPL(10)), info); 117 } 118} 119 120static struct s3c2410_hcd_info smartq_usb_host_info = { 121 .port[0] = { 122 .flags = S3C_HCDFLG_USED 123 }, 124 .port[1] = { 125 .flags = 0 126 }, 127 128 .power_control = smartq_usb_host_powercontrol, 129 .enable_oc = smartq_usb_host_enableoc, 130}; 131 132static struct gpio_vbus_mach_info smartq_usb_otg_vbus_pdata = { 133 .gpio_vbus = S3C64XX_GPL(9), 134 .gpio_pullup = -1, 135 .gpio_vbus_inverted = true, 136}; 137 138static struct platform_device smartq_usb_otg_vbus_dev = { 139 .name = "gpio-vbus", 140 .dev.platform_data = &smartq_usb_otg_vbus_pdata, 141}; 142 143static struct pwm_lookup smartq_pwm_lookup[] = { 144 PWM_LOOKUP("samsung-pwm", 1, "pwm-backlight.0", NULL, 145 1000000000 / (1000 * 20), PWM_POLARITY_NORMAL), 146}; 147 148static int smartq_bl_init(struct device *dev) 149{ 150 s3c_gpio_cfgpin(S3C64XX_GPF(15), S3C_GPIO_SFN(2)); 151 152 return 0; 153} 154 155static struct platform_pwm_backlight_data smartq_backlight_data = { 156 .max_brightness = 1000, 157 .dft_brightness = 600, 158 .enable_gpio = -1, 159 .init = smartq_bl_init, 160}; 161 162static struct platform_device smartq_backlight_device = { 163 .name = "pwm-backlight", 164 .dev = { 165 .parent = &samsung_device_pwm.dev, 166 .platform_data = &smartq_backlight_data, 167 }, 168}; 169 170static struct s3c2410_ts_mach_info smartq_touchscreen_pdata __initdata = { 171 .delay = 65535, 172 .presc = 99, 173 .oversampling_shift = 4, 174}; 175 176static struct s3c_sdhci_platdata smartq_internal_hsmmc_pdata = { 177 .max_width = 4, 178 .cd_type = S3C_SDHCI_CD_PERMANENT, 179}; 180 181static struct s3c_hwmon_pdata smartq_hwmon_pdata __initdata = { 182 /* Battery voltage (?-4.2V) */ 183 .in[0] = &(struct s3c_hwmon_chcfg) { 184 .name = "smartq:battery-voltage", 185 .mult = 3300, 186 .div = 2048, 187 }, 188 /* Reference voltage (1.2V) */ 189 .in[1] = &(struct s3c_hwmon_chcfg) { 190 .name = "smartq:reference-voltage", 191 .mult = 3300, 192 .div = 4096, 193 }, 194}; 195 196static struct dwc2_hsotg_plat smartq_hsotg_pdata; 197 198static int __init smartq_lcd_setup_gpio(void) 199{ 200 int ret; 201 202 ret = gpio_request(S3C64XX_GPM(3), "LCD power"); 203 if (ret < 0) 204 return ret; 205 206 /* turn power off */ 207 gpio_direction_output(S3C64XX_GPM(3), 0); 208 209 return 0; 210} 211 212/* GPM0 -> CS */ 213static struct spi_gpio_platform_data smartq_lcd_control = { 214 .sck = S3C64XX_GPM(1), 215 .mosi = S3C64XX_GPM(2), 216 .miso = S3C64XX_GPM(2), 217}; 218 219static struct platform_device smartq_lcd_control_device = { 220 .name = "spi-gpio", 221 .id = 1, 222 .dev.platform_data = &smartq_lcd_control, 223}; 224 225static void smartq_lcd_power_set(struct plat_lcd_data *pd, unsigned int power) 226{ 227 gpio_direction_output(S3C64XX_GPM(3), power); 228} 229 230static struct plat_lcd_data smartq_lcd_power_data = { 231 .set_power = smartq_lcd_power_set, 232}; 233 234static struct platform_device smartq_lcd_power_device = { 235 .name = "platform-lcd", 236 .dev.parent = &s3c_device_fb.dev, 237 .dev.platform_data = &smartq_lcd_power_data, 238}; 239 240static struct i2c_board_info smartq_i2c_devs[] __initdata = { 241 { I2C_BOARD_INFO("wm8987", 0x1a), }, 242}; 243 244static struct platform_device *smartq_devices[] __initdata = { 245 &s3c_device_hsmmc1, /* Init iNAND first, ... */ 246 &s3c_device_hsmmc0, /* ... then the external SD card */ 247 &s3c_device_hsmmc2, 248 &s3c_device_adc, 249 &s3c_device_fb, 250 &s3c_device_hwmon, 251 &s3c_device_i2c0, 252 &s3c_device_ohci, 253 &s3c_device_rtc, 254 &samsung_device_pwm, 255 &s3c_device_ts, 256 &s3c_device_usb_hsotg, 257 &s3c64xx_device_iis0, 258 &smartq_backlight_device, 259 &smartq_lcd_control_device, 260 &smartq_lcd_power_device, 261 &smartq_usb_otg_vbus_dev, 262}; 263 264static void __init smartq_lcd_mode_set(void) 265{ 266 u32 tmp; 267 268 /* set the LCD type */ 269 tmp = __raw_readl(S3C64XX_SPCON); 270 tmp &= ~S3C64XX_SPCON_LCD_SEL_MASK; 271 tmp |= S3C64XX_SPCON_LCD_SEL_RGB; 272 __raw_writel(tmp, S3C64XX_SPCON); 273 274 /* remove the LCD bypass */ 275 tmp = __raw_readl(S3C64XX_MODEM_MIFPCON); 276 tmp &= ~MIFPCON_LCD_BYPASS; 277 __raw_writel(tmp, S3C64XX_MODEM_MIFPCON); 278} 279 280static void smartq_power_off(void) 281{ 282 gpio_direction_output(S3C64XX_GPK(15), 1); 283} 284 285static int __init smartq_power_off_init(void) 286{ 287 int ret; 288 289 ret = gpio_request(S3C64XX_GPK(15), "Power control"); 290 if (ret < 0) { 291 pr_err("%s: failed to get GPK15\n", __func__); 292 return ret; 293 } 294 295 /* leave power on */ 296 gpio_direction_output(S3C64XX_GPK(15), 0); 297 298 pm_power_off = smartq_power_off; 299 300 return ret; 301} 302 303static int __init smartq_usb_host_init(void) 304{ 305 int ret; 306 307 ret = gpio_request(S3C64XX_GPL(0), "USB power control"); 308 if (ret < 0) { 309 pr_err("%s: failed to get GPL0\n", __func__); 310 return ret; 311 } 312 313 ret = gpio_request(S3C64XX_GPL(1), "USB host power control"); 314 if (ret < 0) { 315 pr_err("%s: failed to get GPL1\n", __func__); 316 goto err; 317 } 318 319 if (!machine_is_smartq5()) { 320 /* This isn't present on a SmartQ 5 board */ 321 ret = gpio_request(S3C64XX_GPL(10), "USB host overcurrent"); 322 if (ret < 0) { 323 pr_err("%s: failed to get GPL10\n", __func__); 324 goto err2; 325 } 326 } 327 328 /* turn power off */ 329 gpio_direction_output(S3C64XX_GPL(0), 0); 330 gpio_direction_output(S3C64XX_GPL(1), 0); 331 if (!machine_is_smartq5()) 332 gpio_direction_input(S3C64XX_GPL(10)); 333 334 s3c_device_ohci.dev.platform_data = &smartq_usb_host_info; 335 336 return 0; 337 338err2: 339 gpio_free(S3C64XX_GPL(1)); 340err: 341 gpio_free(S3C64XX_GPL(0)); 342 return ret; 343} 344 345static int __init smartq_wifi_init(void) 346{ 347 int ret; 348 349 ret = gpio_request(S3C64XX_GPK(1), "wifi control"); 350 if (ret < 0) { 351 pr_err("%s: failed to get GPK1\n", __func__); 352 return ret; 353 } 354 355 ret = gpio_request(S3C64XX_GPK(2), "wifi reset"); 356 if (ret < 0) { 357 pr_err("%s: failed to get GPK2\n", __func__); 358 gpio_free(S3C64XX_GPK(1)); 359 return ret; 360 } 361 362 /* turn power on */ 363 gpio_direction_output(S3C64XX_GPK(1), 1); 364 365 /* reset device */ 366 gpio_direction_output(S3C64XX_GPK(2), 0); 367 mdelay(100); 368 gpio_set_value(S3C64XX_GPK(2), 1); 369 gpio_direction_input(S3C64XX_GPK(2)); 370 371 return 0; 372} 373 374static struct map_desc smartq_iodesc[] __initdata = {}; 375void __init smartq_map_io(void) 376{ 377 s3c64xx_init_io(smartq_iodesc, ARRAY_SIZE(smartq_iodesc)); 378 s3c64xx_set_xtal_freq(12000000); 379 s3c64xx_set_xusbxti_freq(12000000); 380 s3c24xx_init_uarts(smartq_uartcfgs, ARRAY_SIZE(smartq_uartcfgs)); 381 samsung_set_timer_source(SAMSUNG_PWM3, SAMSUNG_PWM4); 382 383 smartq_lcd_mode_set(); 384} 385 386void __init smartq_machine_init(void) 387{ 388 s3c_i2c0_set_platdata(NULL); 389 dwc2_hsotg_set_platdata(&smartq_hsotg_pdata); 390 s3c_hwmon_set_platdata(&smartq_hwmon_pdata); 391 s3c_sdhci1_set_platdata(&smartq_internal_hsmmc_pdata); 392 s3c_sdhci2_set_platdata(&smartq_internal_hsmmc_pdata); 393 s3c24xx_ts_set_platdata(&smartq_touchscreen_pdata); 394 395 i2c_register_board_info(0, smartq_i2c_devs, 396 ARRAY_SIZE(smartq_i2c_devs)); 397 398 WARN_ON(smartq_lcd_setup_gpio()); 399 WARN_ON(smartq_power_off_init()); 400 WARN_ON(smartq_usb_host_init()); 401 WARN_ON(smartq_wifi_init()); 402 403 pwm_add_table(smartq_pwm_lookup, ARRAY_SIZE(smartq_pwm_lookup)); 404 platform_add_devices(smartq_devices, ARRAY_SIZE(smartq_devices)); 405} 406