Root/
| 1 | /* |
| 2 | * linux/arch/mips/jz4740/board-a320.c |
| 3 | * |
| 4 | * JZ4740 A320 board setup routines. |
| 5 | * |
| 6 | * Copyright (c) 2006-2007 Ingenic Semiconductor Inc. |
| 7 | * Copyright (c) 2009 Ignacio Garcia Perez <iggarpe@gmail.com> |
| 8 | * Copyright (c) 2010-2011 Maarten ter Huurne <maarten@treewalker.org> |
| 9 | * |
| 10 | * This program is free software; you can redistribute it and/or modify |
| 11 | * it under the terms of the GNU General Public License version 2 as |
| 12 | * published by the Free Software Foundation. |
| 13 | */ |
| 14 | |
| 15 | #include <linux/init.h> |
| 16 | #include <linux/sched.h> |
| 17 | #include <linux/ioport.h> |
| 18 | #include <linux/mm.h> |
| 19 | #include <linux/console.h> |
| 20 | #include <linux/delay.h> |
| 21 | #include <linux/kernel.h> |
| 22 | #include <linux/gpio.h> |
| 23 | #include <linux/i2c.h> |
| 24 | #include <linux/i2c-gpio.h> |
| 25 | #include <linux/power_supply.h> |
| 26 | #include <linux/power/gpio-charger.h> |
| 27 | #include <linux/power/jz4740-battery.h> |
| 28 | |
| 29 | #include <linux/pwm.h> |
| 30 | #include <linux/pwm_backlight.h> |
| 31 | #include <linux/input.h> |
| 32 | #include <linux/gpio_keys.h> |
| 33 | |
| 34 | #include <media/radio-rda5807.h> |
| 35 | |
| 36 | #include <asm/cpu.h> |
| 37 | #include <asm/bootinfo.h> |
| 38 | #include <asm/mipsregs.h> |
| 39 | #include <asm/reboot.h> |
| 40 | |
| 41 | #include <asm/mach-jz4740/gpio.h> |
| 42 | #include <asm/mach-jz4740/jz4740_fb.h> |
| 43 | #include <asm/mach-jz4740/jz4740_mmc.h> |
| 44 | #include <asm/mach-jz4740/jz4740_nand.h> |
| 45 | #include <asm/mach-jz4740/platform.h> |
| 46 | |
| 47 | #include "clock.h" |
| 48 | |
| 49 | /* |
| 50 | * This is called by the panic reboot delay loop if panic=<n> parameter |
| 51 | * is passed to the kernel. The A320 does not have any LEDs, so the best |
| 52 | * we can do is to blink the LCD backlight. |
| 53 | * |
| 54 | * TODO(MtH): This should use the backlight driver instead of directly |
| 55 | * manipulating the GPIO pin. |
| 56 | */ |
| 57 | static long a320_panic_blink_callback(int time) |
| 58 | { |
| 59 | gpio_direction_output(JZ_GPIO_PORTD(31), (time / 500) & 1); |
| 60 | return 0; |
| 61 | } |
| 62 | |
| 63 | #ifdef CONFIG_I2C_GPIO |
| 64 | /* I2C over GPIO pins */ |
| 65 | static struct i2c_gpio_platform_data a320_i2c_pdata = { |
| 66 | .sda_pin = JZ_GPIO_PORTD(23), |
| 67 | .scl_pin = JZ_GPIO_PORTD(24), |
| 68 | .udelay = 2, |
| 69 | .timeout = 3 * HZ, |
| 70 | }; |
| 71 | |
| 72 | static struct platform_device a320_i2c_device = { |
| 73 | .name = "i2c-gpio", |
| 74 | .id = 0, |
| 75 | .dev = { |
| 76 | .platform_data = &a320_i2c_pdata, |
| 77 | }, |
| 78 | }; |
| 79 | #endif |
| 80 | |
| 81 | /* NAND */ |
| 82 | #define A320_NAND_PAGE_SIZE (4096ull) |
| 83 | #define A320_NAND_ERASE_BLOCK_SIZE (128 * A320_NAND_PAGE_SIZE) |
| 84 | |
| 85 | static struct mtd_partition a320_nand_partitions[] = { |
| 86 | { .name = "SPL", |
| 87 | .offset = 0 * A320_NAND_ERASE_BLOCK_SIZE, |
| 88 | .size = 64 * A320_NAND_PAGE_SIZE, |
| 89 | }, |
| 90 | { .name = "uC/OS-II loader", |
| 91 | .offset = 64 * A320_NAND_PAGE_SIZE, |
| 92 | .size = 192 * A320_NAND_PAGE_SIZE, |
| 93 | }, |
| 94 | /* erase block 3 is empty (maybe alternative location for bbt?) */ |
| 95 | /* erase block 4 contains the bad block table */ |
| 96 | { .name = "uC/OS-II Z:", |
| 97 | .offset = 5 * A320_NAND_ERASE_BLOCK_SIZE, |
| 98 | .size = 127 * A320_NAND_ERASE_BLOCK_SIZE, |
| 99 | }, |
| 100 | { .name = "uC/OS-II A:", |
| 101 | .offset = 132 * A320_NAND_ERASE_BLOCK_SIZE, |
| 102 | .size = (8192 - 132) * A320_NAND_ERASE_BLOCK_SIZE, |
| 103 | }, |
| 104 | }; |
| 105 | |
| 106 | static uint8_t a320_nand_bbt_pattern[] = {'b', 'b', 't', '8' }; |
| 107 | |
| 108 | static struct nand_bbt_descr a320_nand_bbt_main_descr = { |
| 109 | .options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT, |
| 110 | /* TODO(MtH): Maybe useful flags for the future: |
| 111 | NAND_BBT_CREATE | NAND_BBT_WRITE | NAND_BBT_VERSION | NAND_BBT_PERCHIP |
| 112 | */ |
| 113 | .pages = { 4 * A320_NAND_ERASE_BLOCK_SIZE / A320_NAND_PAGE_SIZE }, |
| 114 | .maxblocks = 1, |
| 115 | .pattern = a320_nand_bbt_pattern, |
| 116 | .len = ARRAY_SIZE(a320_nand_bbt_pattern), |
| 117 | .offs = 128 - ARRAY_SIZE(a320_nand_bbt_pattern), |
| 118 | }; |
| 119 | |
| 120 | static struct nand_ecclayout a320_nand_ecc_layout = { |
| 121 | .eccbytes = 72, |
| 122 | .eccpos = { |
| 123 | 4, 5, 6, 7, 8, 9, 10, 11, 12, /* sector 0 */ |
| 124 | 16, 17, 18, 19, 20, 21, 22, 23, 24, /* sector 1 */ |
| 125 | 28, 29, 30, 31, 32, 33, 34, 35, 36, /* sector 2 */ |
| 126 | 40, 41, 42, 43, 44, 45, 46, 47, 48, /* sector 3 */ |
| 127 | 52, 53, 54, 55, 56, 57, 58, 59, 60, /* sector 4 */ |
| 128 | 64, 65, 66, 67, 68, 69, 70, 71, 72, /* sector 5 */ |
| 129 | 76, 77, 78, 79, 80, 81, 82, 83, 84, /* sector 6 */ |
| 130 | 88, 89, 90, 91, 92, 93, 94, 95, 96, /* sector 7 */ |
| 131 | }, |
| 132 | .oobfree = { |
| 133 | { .offset = 100, .length = 22 }, |
| 134 | } |
| 135 | }; |
| 136 | |
| 137 | static void a320_nand_ident(struct platform_device *pdev, |
| 138 | struct nand_chip *chip, |
| 139 | struct mtd_partition **partitions, |
| 140 | int *num_partitions) |
| 141 | { |
| 142 | chip->bbt_options |= NAND_BBT_USE_FLASH; |
| 143 | chip->bbt_td = &a320_nand_bbt_main_descr; |
| 144 | /* MtH: I did not find a mirror bbt yet, but it might exist. */ |
| 145 | chip->bbt_md = NULL; |
| 146 | } |
| 147 | |
| 148 | static struct jz_nand_platform_data a320_nand_pdata = { |
| 149 | .num_partitions = ARRAY_SIZE(a320_nand_partitions), |
| 150 | .partitions = a320_nand_partitions, |
| 151 | .ecc_layout = &a320_nand_ecc_layout, |
| 152 | .busy_gpio = JZ_GPIO_PORTC(30), |
| 153 | .banks = { 1, 2, 3, 4 }, |
| 154 | .ident_callback = a320_nand_ident, |
| 155 | }; |
| 156 | |
| 157 | /* Display */ |
| 158 | static struct fb_videomode a320_video_modes[] = { |
| 159 | { |
| 160 | .name = "320x240", |
| 161 | .xres = 320, |
| 162 | .yres = 240, |
| 163 | // TODO(MtH): Set refresh or pixclock. |
| 164 | .vmode = FB_VMODE_NONINTERLACED, |
| 165 | }, |
| 166 | }; |
| 167 | |
| 168 | static struct jz4740_fb_platform_data a320_fb_pdata = { |
| 169 | .width = 60, |
| 170 | .height = 45, |
| 171 | .num_modes = ARRAY_SIZE(a320_video_modes), |
| 172 | .modes = a320_video_modes, |
| 173 | .bpp = 16, |
| 174 | .lcd_type = JZ_LCD_TYPE_SMART_PARALLEL_16_BIT, |
| 175 | .pixclk_falling_edge = 0, |
| 176 | .chip_select_active_low = 1, |
| 177 | .register_select_active_low = 1, |
| 178 | }; |
| 179 | |
| 180 | static int a320_backlight_notify(struct device *dev, int brightness) |
| 181 | { |
| 182 | if (!gpio_get_value(JZ_GPIO_PORTB(18))) { |
| 183 | /* RESET_N pin of the ILI chip is pulled down, |
| 184 | so force backlight off. */ |
| 185 | return 0; |
| 186 | } |
| 187 | |
| 188 | return brightness; |
| 189 | } |
| 190 | |
| 191 | static struct platform_pwm_backlight_data a320_backlight_pdata = { |
| 192 | .max_brightness = 255, |
| 193 | .dft_brightness = 145, |
| 194 | .pwm_period_ns = 1000000, |
| 195 | .notify = a320_backlight_notify, |
| 196 | }; |
| 197 | |
| 198 | static struct platform_device a320_backlight_device = { |
| 199 | .name = "pwm-backlight", |
| 200 | .id = -1, |
| 201 | .dev = { |
| 202 | .platform_data = &a320_backlight_pdata, |
| 203 | }, |
| 204 | }; |
| 205 | |
| 206 | struct pwm_lookup a320_pwm_table[] = { |
| 207 | PWM_LOOKUP("jz4740-pwm", 7, "pwm-backlight", 0), |
| 208 | }; |
| 209 | |
| 210 | static struct jz4740_mmc_platform_data a320_mmc_pdata = { |
| 211 | .gpio_card_detect = JZ_GPIO_PORTB(29), |
| 212 | .gpio_read_only = -1, |
| 213 | .gpio_power = -1, |
| 214 | // TODO(MtH): I don't know which GPIO pin the SD power is connected to. |
| 215 | // Booboo left power alone, but I don't know why. |
| 216 | //.gpio_power = GPIO_SD_VCC_EN_N, |
| 217 | //.power_active_low = 1, |
| 218 | }; |
| 219 | |
| 220 | /* Battery */ |
| 221 | static struct jz_battery_platform_data a320_battery_pdata = { |
| 222 | // TODO(MtH): Sometimes while charging, the GPIO pin quickly flips between |
| 223 | // 0 and 1. This causes a very high CPU load because the kernel |
| 224 | // will invoke a hotplug event handler process on every status |
| 225 | // change. Until it is clear how to avoid or handle that, it |
| 226 | // is better not to use the charge status. |
| 227 | //.gpio_charge = JZ_GPIO_PORTB(30), |
| 228 | .gpio_charge = -1, |
| 229 | .gpio_charge_active_low = 1, |
| 230 | .info = { |
| 231 | .name = "battery", |
| 232 | .technology = POWER_SUPPLY_TECHNOLOGY_LIPO, |
| 233 | .voltage_max_design = 4150000, |
| 234 | .voltage_min_design = 3450000, |
| 235 | }, |
| 236 | }; |
| 237 | |
| 238 | static char *a320_batteries[] = { |
| 239 | "battery", |
| 240 | }; |
| 241 | |
| 242 | static struct gpio_charger_platform_data a320_charger_pdata = { |
| 243 | .name = "usb", |
| 244 | .type = POWER_SUPPLY_TYPE_USB, |
| 245 | .gpio = JZ_GPIO_PORTD(28), |
| 246 | .gpio_active_low = 0, |
| 247 | .supplied_to = a320_batteries, |
| 248 | .num_supplicants = ARRAY_SIZE(a320_batteries), |
| 249 | }; |
| 250 | |
| 251 | static struct platform_device a320_charger_device = { |
| 252 | .name = "gpio-charger", |
| 253 | .dev = { |
| 254 | .platform_data = &a320_charger_pdata, |
| 255 | }, |
| 256 | }; |
| 257 | |
| 258 | /* Note that the microswitch buttons need debounce while the rubber buttons |
| 259 | * do not need it. |
| 260 | */ |
| 261 | static struct gpio_keys_button a320_buttons[] = { |
| 262 | /* D-pad up */ { |
| 263 | .gpio = JZ_GPIO_PORTD(6), |
| 264 | .active_low = 1, |
| 265 | .code = KEY_UP, |
| 266 | }, |
| 267 | /* D-pad down */ { |
| 268 | .gpio = JZ_GPIO_PORTD(27), |
| 269 | .active_low = 1, |
| 270 | .code = KEY_DOWN, |
| 271 | }, |
| 272 | /* D-pad left */ { |
| 273 | .gpio = JZ_GPIO_PORTD(5), |
| 274 | .active_low = 1, |
| 275 | .code = KEY_LEFT, |
| 276 | }, |
| 277 | /* D-pad right */ { |
| 278 | .gpio = JZ_GPIO_PORTD(18), |
| 279 | .active_low = 1, |
| 280 | .code = KEY_RIGHT, |
| 281 | }, |
| 282 | /* A button */ { |
| 283 | .gpio = JZ_GPIO_PORTD(0), |
| 284 | .active_low = 1, |
| 285 | .code = KEY_LEFTCTRL, |
| 286 | }, |
| 287 | /* B button */ { |
| 288 | .gpio = JZ_GPIO_PORTD(1), |
| 289 | .active_low = 1, |
| 290 | .code = KEY_LEFTALT, |
| 291 | }, |
| 292 | /* X button */ { |
| 293 | .gpio = JZ_GPIO_PORTD(19), |
| 294 | .active_low = 1, |
| 295 | .code = KEY_SPACE, |
| 296 | }, |
| 297 | /* Y button */ { |
| 298 | .gpio = JZ_GPIO_PORTD(2), |
| 299 | .active_low = 1, |
| 300 | .code = KEY_LEFTSHIFT, |
| 301 | }, |
| 302 | /* Left shoulder button */ { |
| 303 | .gpio = JZ_GPIO_PORTD(14), |
| 304 | .active_low = 1, |
| 305 | .code = KEY_TAB, |
| 306 | .debounce_interval = 5, |
| 307 | }, |
| 308 | /* Right shoulder button */ { |
| 309 | .gpio = JZ_GPIO_PORTD(15), |
| 310 | .active_low = 1, |
| 311 | .code = KEY_BACKSPACE, |
| 312 | .debounce_interval = 5, |
| 313 | }, |
| 314 | /* START button */ { |
| 315 | .gpio = JZ_GPIO_PORTC(17), |
| 316 | .active_low = 1, |
| 317 | .code = KEY_ENTER, |
| 318 | .debounce_interval = 5, |
| 319 | }, |
| 320 | /* SELECT button */ { |
| 321 | .gpio = JZ_GPIO_PORTD(17), |
| 322 | .active_low = 1, |
| 323 | .code = KEY_ESC, |
| 324 | .debounce_interval = 5, |
| 325 | }, |
| 326 | /* POWER slider */ { |
| 327 | .gpio = JZ_GPIO_PORTD(29), |
| 328 | .active_low = 1, |
| 329 | .code = KEY_POWER, |
| 330 | .wakeup = 1, |
| 331 | }, |
| 332 | /* POWER hold */ { |
| 333 | .gpio = JZ_GPIO_PORTD(22), |
| 334 | .active_low = 1, |
| 335 | .code = KEY_PAUSE, |
| 336 | }, |
| 337 | }; |
| 338 | |
| 339 | static struct gpio_keys_platform_data a320_gpio_keys_pdata = { |
| 340 | .buttons = a320_buttons, |
| 341 | .nbuttons = ARRAY_SIZE(a320_buttons), |
| 342 | .rep = 1, |
| 343 | }; |
| 344 | |
| 345 | static struct platform_device a320_gpio_keys_device = { |
| 346 | .name = "gpio-keys", |
| 347 | .id = -1, |
| 348 | .dev = { |
| 349 | .platform_data = &a320_gpio_keys_pdata, |
| 350 | }, |
| 351 | }; |
| 352 | |
| 353 | /* Audio */ |
| 354 | static struct platform_device a320_audio_device = { |
| 355 | .name = "a320-audio", |
| 356 | .id = -1, |
| 357 | }; |
| 358 | |
| 359 | static struct platform_device *jz_platform_devices[] __initdata = { |
| 360 | #ifdef CONFIG_I2C_JZ47XX |
| 361 | &jz4740_i2c_device, |
| 362 | #endif |
| 363 | #ifdef CONFIG_I2C_GPIO |
| 364 | &a320_i2c_device, |
| 365 | #endif |
| 366 | /* USB host is not usable since the PCB does not route the pins to |
| 367 | * a place where new wires can be soldered. */ |
| 368 | /*&jz4740_usb_ohci_device,*/ |
| 369 | &jz4740_udc_device, |
| 370 | &jz4740_mmc_device, |
| 371 | &jz4740_nand_device, |
| 372 | &jz4740_framebuffer_device, |
| 373 | &jz4740_pcm_device, |
| 374 | &jz4740_i2s_device, |
| 375 | &jz4740_codec_device, |
| 376 | &jz4740_rtc_device, |
| 377 | &jz4740_adc_device, |
| 378 | &jz4740_wdt_device, |
| 379 | &a320_charger_device, |
| 380 | &jz4740_pwm_device, |
| 381 | &a320_backlight_device, |
| 382 | &a320_gpio_keys_device, |
| 383 | &a320_audio_device, |
| 384 | }; |
| 385 | |
| 386 | static void __init board_gpio_setup(void) |
| 387 | { |
| 388 | /* We only need to enable/disable pullup here for pins used in generic |
| 389 | * drivers. Everything else is done by the drivers themselves. */ |
| 390 | |
| 391 | /* Disable pullup of the USB detection pin: on the A320 pullup or not |
| 392 | * seems to make no difference, but on A330 the signal will be unstable |
| 393 | * when the pullup is enabled. */ |
| 394 | jz_gpio_disable_pullup(JZ_GPIO_PORTD(28)); |
| 395 | } |
| 396 | |
| 397 | static int __init a320_init_platform_devices(void) |
| 398 | { |
| 399 | jz4740_framebuffer_device.dev.platform_data = &a320_fb_pdata; |
| 400 | jz4740_nand_device.dev.platform_data = &a320_nand_pdata; |
| 401 | jz4740_adc_device.dev.platform_data = &a320_battery_pdata; |
| 402 | jz4740_mmc_device.dev.platform_data = &a320_mmc_pdata; |
| 403 | |
| 404 | jz4740_serial_device_register(); |
| 405 | |
| 406 | pwm_add_table(a320_pwm_table, ARRAY_SIZE(a320_pwm_table)); |
| 407 | |
| 408 | return platform_add_devices(jz_platform_devices, |
| 409 | ARRAY_SIZE(jz_platform_devices)); |
| 410 | } |
| 411 | |
| 412 | struct jz4740_clock_board_data jz4740_clock_bdata = { |
| 413 | .ext_rate = 12000000, |
| 414 | .rtc_rate = 32768, |
| 415 | }; |
| 416 | |
| 417 | static struct rda5807_platform_data a320_rda5807_pdata = { |
| 418 | .input_flags = RDA5807_INPUT_LNA_WC_25 | RDA5807_LNA_PORT_P, |
| 419 | .output_flags = RDA5807_OUTPUT_AUDIO_ANALOG, |
| 420 | }; |
| 421 | |
| 422 | static struct i2c_board_info a320_i2c_info[] __initdata = { |
| 423 | { |
| 424 | .type = "radio-rda5807", |
| 425 | .addr = RDA5807_I2C_ADDR, |
| 426 | .platform_data = &a320_rda5807_pdata, |
| 427 | } |
| 428 | }; |
| 429 | |
| 430 | static int __init a320_board_setup(void) |
| 431 | { |
| 432 | printk(KERN_INFO "JZ4740 A320 board setup\n"); |
| 433 | |
| 434 | panic_blink = a320_panic_blink_callback; |
| 435 | |
| 436 | board_gpio_setup(); |
| 437 | |
| 438 | if (a320_init_platform_devices()) |
| 439 | panic("Failed to initalize platform devices\n"); |
| 440 | |
| 441 | i2c_register_board_info(0, a320_i2c_info, ARRAY_SIZE(a320_i2c_info)); |
| 442 | |
| 443 | return 0; |
| 444 | } |
| 445 | |
| 446 | arch_initcall(a320_board_setup); |
| 447 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9
