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